diff --git a/src/main/java/uk/org/floop/epq3d/Face.java b/src/main/java/uk/org/floop/epq3d/Face.java index 120ece6..6dcd904 100644 --- a/src/main/java/uk/org/floop/epq3d/Face.java +++ b/src/main/java/uk/org/floop/epq3d/Face.java @@ -1,7 +1,6 @@ package uk.org.floop.epq3d; import java.awt.image.BufferedImage; -import java.util.ArrayList; public class Face { public PointComp[] points; @@ -17,8 +16,8 @@ public Face fixedFace; public void initialise(){ calculateNormal(); + perspectiveMappingMatrices = new Matrix[points.length - 2][2]; separateTris(); - perspectiveMappingMatrices = new Matrix[tris.length][2]; generateUVMatrices(); // fixedFace is a constant object which is used temporarily when the face intersects with the camera, // and thus needs to be sliced. @@ -46,14 +45,178 @@ // check for backface culling has been done previously. // initialise points int numberOfPixels = 0; + // apply point transforms for all points within the face boolean valid = applyPointTransforms(camMatrix, FPDis, scrX, scrY); // this function is completed every frame, without checking whether pixels will be drawn first bakePerspectiveMatrices(img, zBuf, debugImg, camMatrix, FPDis, scrX, scrY); + // 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); + } + } else { + /*todo check wether there is any slowdown here + it might be that the compiler just sorts it but i'm not 100% sure. + fix would be to change it so that I only need to loop through points once, and also reduce the number + of temporary point objects i use */ + for (int tri_i = 0; tri_i < trisFaceList.length; tri_i += 1) { + // 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) { + 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); + } 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) { + invalidIndex = i; + break; + } + } + if (invalidIndex == -1) { + throw new RuntimeException("How did this happen?!"); + } + Point3D oldPoint1 = points[trisFaceList[tri_i][Math.floorMod(invalidIndex + 1, 3)]].getRotatedPoint(); + Point3D oldPoint2 = points[trisFaceList[tri_i][Math.floorMod(invalidIndex + 2, 3)]].getRotatedPoint(); + Point3D invalidPoint = points[trisFaceList[tri_i][invalidIndex]].getRotatedPoint(); + Point3D newPoint1; + Point3D newPoint2; + // 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, + // separately in the xz and yz planes. + double gradX = (oldPoint1.z - invalidPoint.z) / + (oldPoint1.x - invalidPoint.x); + double gradY = (oldPoint1.z - invalidPoint.z) / + (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); + if (!Double.isFinite(gradX)) { + newPoint1.x = oldPoint1.x; + } + if (!Double.isFinite(gradY)) { + newPoint1.y = oldPoint1.y; + } + } + + // interpolate between oldPoint2 and invalid point + { + // interpolate between oldPoint2 and invalidPoint + // solving for z = 0.1 for the line between oldPoint2 and invalidPoint, + // separately in the xz and yz planes. + double gradX = (oldPoint2.z - invalidPoint.z) / + (oldPoint2.x - invalidPoint.x); + double gradY = (oldPoint2.z - invalidPoint.z) / + (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); + if (!Double.isFinite(gradX)) { + newPoint2.x = oldPoint2.x; + } + if (!Double.isFinite(gradY)) { + newPoint2.y = oldPoint2.y; + } + } + + Triangle newTri; + newTri = new Triangle(oldPoint1.project(FPDis, scrX, scrY), newPoint2.project(FPDis, scrX, scrY), newPoint1.project(FPDis, scrX, scrY), + new boolean[]{false, false, false}, + tris[tri_i].texture, + tris[tri_i].perspectiveMappingMatrix); + newTri.draw(img, zBuf, FPDis, scrX, scrY); + newTri = new Triangle(oldPoint1.project(FPDis, scrX, scrY), oldPoint2.project(FPDis, scrX, scrY), newPoint2.project(FPDis, scrX, scrY), + new boolean[]{false, false, false}, + tris[tri_i].texture, + tris[tri_i].perspectiveMappingMatrix); + newTri.draw(img, zBuf, FPDis, scrX, scrY); + } 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) { + validIndex = i; + break; + } + } + if (validIndex == -1) { + throw new RuntimeException("How did this happen?!"); + } + Point3D invalidPoint1 = points[trisFaceList[tri_i][Math.floorMod(validIndex + 1, 3)]].getRotatedPoint(); + Point3D invalidPoint2 = points[trisFaceList[tri_i][Math.floorMod(validIndex + 2, 3)]].getRotatedPoint(); + Point3D oldPoint = points[trisFaceList[tri_i][validIndex]].getRotatedPoint(); + Point3D newPoint1; + Point3D newPoint2; + // interpolate for z = 0.1 between invalid1 and oldPoint + { + double gradX = (oldPoint.z - invalidPoint1.z) / + (oldPoint.x - invalidPoint1.x); + double gradY = (oldPoint.z - invalidPoint1.z) / + (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); + if (!Double.isFinite(gradX)) { + newPoint1.x = oldPoint.x; + } + if (!Double.isFinite(gradY)) { + newPoint1.y = oldPoint.y; + } + } + // interpolate for z = 0.1 between invalid2 and oldPoint + { + double gradX = (oldPoint.z - invalidPoint2.z) / + (oldPoint.x - invalidPoint2.x); + double gradY = (oldPoint.z - invalidPoint2.z) / + (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); + if (!Double.isFinite(gradX)) { + newPoint2.x = oldPoint.x; + } + if (!Double.isFinite(gradY)) { + newPoint2.y = oldPoint.y; + } + } + + // 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), + new boolean[]{true, true, true}, + tris[tri_i].texture, + tris[tri_i].perspectiveMappingMatrix); + newTri.draw(img, zBuf, FPDis, scrX, scrY); + } // if all points are invalid, do nothing + } + } + return numberOfPixels; + } + + +/* if (valid) { - drawTris(img, zBuf, FPDis, scrX, scrY);} + //drawTris(img, zBuf, FPDis, scrX, scrY);} else { ArrayList newPoints = new ArrayList<>(); + // if there are points behind the camera, loop through all the points and interpolate a point that is. // The perspective mapping matrix is calculated beforehand, so we don't need to move UVS PointComp lastPoint = points[points.length - 1]; @@ -116,13 +279,7 @@ } return numberOfPixels; } - - public void drawTris(BufferedImage img, BufferedImage zBuf, double FPDis, int scrX, int scrY) { - for (Triangle tri : - tris) { - tri.draw(img, zBuf, FPDis, scrX, scrY); - } - } +*/ public boolean applyPointTransforms(Matrix camMatrix, double FPDis, int scrX, int scrY){ boolean valid = true; for (PointComp point: @@ -130,10 +287,12 @@ point.setRotatedPoint(camMatrix); // if any points are behind the camera, we will need to handle it differently. if(point.getRotatedPoint().z < 0.1){ - valid = false;} - // only worth calculating the projected point if they are all valid - if(valid){ - point.setProjectedPoint(FPDis, scrX, scrY);} + valid = false; + } + else{ + // only worth calculating the projected point if that point is valid + point.setProjectedPoint(FPDis, scrX, scrY); + } } return valid; } @@ -142,7 +301,7 @@ int[][] newTrisFaceList = new int[points.length - 2][3]; for(int i = 0; i< newTris.length; i+=1){ - newTrisFaceList[i][0] = i; + newTrisFaceList[i][0] = 0; newTrisFaceList[i][1] = i+1; newTrisFaceList[i][2] = i+2; diff --git a/src/main/java/uk/org/floop/epq3d/Screen.java b/src/main/java/uk/org/floop/epq3d/Screen.java index 26fc24e..7a56f29 100644 --- a/src/main/java/uk/org/floop/epq3d/Screen.java +++ b/src/main/java/uk/org/floop/epq3d/Screen.java @@ -7,6 +7,8 @@ public class Screen extends JPanel implements ActionListener, KeyListener, MouseListener, MouseMotionListener { // __working variables__ + + public boolean initialised = false; public long lastTime = 0; // mouse private boolean captured = false; @@ -65,15 +67,14 @@ // this frameTimer will call the actionPerformed() method every DELAY ms frameTimer = new Timer(DELAY, this); frameTimer.start(); + initialised = true; } @Override public void actionPerformed(ActionEvent e) { - // called by the frameTimer every DELAY ms. - // runs the tick of the player before the board is redrawn + // run the tick of the player before the board is redrawn player.tick(get_mouse_rel()); - // calling repaint() will trigger paintComponent() to run again, - // which will refresh/redraw the graphics. + // draw screen repaint(); } @@ -88,7 +89,6 @@ // draw graphics. drawScreen(g); // player.draw(g, this); - // this smooths out animations on some systems Toolkit.getDefaultToolkit().sync(); } diff --git a/src/main/java/uk/org/floop/epq3d/Triangle.java b/src/main/java/uk/org/floop/epq3d/Triangle.java index 9467655..412da4c 100644 --- a/src/main/java/uk/org/floop/epq3d/Triangle.java +++ b/src/main/java/uk/org/floop/epq3d/Triangle.java @@ -41,7 +41,6 @@ // long lastMillis; - if (!is_initialised){initialise();} int[] point1; int[] point2;