diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..7b72545
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__com_googlecode_json_simple_json_simple_1_1.xml b/.idea/libraries/Maven__com_googlecode_json_simple_json_simple_1_1.xml
new file mode 100644
index 0000000..ea70fe1
--- /dev/null
+++ b/.idea/libraries/Maven__com_googlecode_json_simple_json_simple_1_1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..e8d124d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..7130ed8
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EPQ 3D renderer.iml b/EPQ 3D renderer.iml
new file mode 100644
index 0000000..e9c3f97
--- /dev/null
+++ b/EPQ 3D renderer.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/out/production/EPQ 3D renderer/App.class b/out/production/EPQ 3D renderer/App.class
deleted file mode 100644
index 8648eab..0000000
--- a/out/production/EPQ 3D renderer/App.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Face.class b/out/production/EPQ 3D renderer/Face.class
deleted file mode 100644
index b1aba64..0000000
--- a/out/production/EPQ 3D renderer/Face.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Matrix.class b/out/production/EPQ 3D renderer/Matrix.class
deleted file mode 100644
index 0702f87..0000000
--- a/out/production/EPQ 3D renderer/Matrix.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Object3d.class b/out/production/EPQ 3D renderer/Object3d.class
deleted file mode 100644
index 95c5e2b..0000000
--- a/out/production/EPQ 3D renderer/Object3d.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Player.class b/out/production/EPQ 3D renderer/Player.class
deleted file mode 100644
index 2fa269f..0000000
--- a/out/production/EPQ 3D renderer/Player.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Point2D.class b/out/production/EPQ 3D renderer/Point2D.class
deleted file mode 100644
index 2ef102e..0000000
--- a/out/production/EPQ 3D renderer/Point2D.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Point3D.class b/out/production/EPQ 3D renderer/Point3D.class
deleted file mode 100644
index a62c208..0000000
--- a/out/production/EPQ 3D renderer/Point3D.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/PointComp.class b/out/production/EPQ 3D renderer/PointComp.class
deleted file mode 100644
index 4cfb75a..0000000
--- a/out/production/EPQ 3D renderer/PointComp.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Screen.class b/out/production/EPQ 3D renderer/Screen.class
deleted file mode 100644
index 5a48ebd..0000000
--- a/out/production/EPQ 3D renderer/Screen.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Triangle.class b/out/production/EPQ 3D renderer/Triangle.class
deleted file mode 100644
index c4afc42..0000000
--- a/out/production/EPQ 3D renderer/Triangle.class
+++ /dev/null
Binary files differ
diff --git a/out/production/EPQ 3D renderer/Vector3D.class b/out/production/EPQ 3D renderer/Vector3D.class
deleted file mode 100644
index 79370e5..0000000
--- a/out/production/EPQ 3D renderer/Vector3D.class
+++ /dev/null
Binary files differ
diff --git a/src/main/java/App.java b/src/main/java/App.java
deleted file mode 100644
index 2f7f969..0000000
--- a/src/main/java/App.java
+++ /dev/null
@@ -1,113 +0,0 @@
-import javax.imageio.ImageIO;
-import javax.swing.*;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-
-class App {
- static ObjectCollection mainCollection;
- private static void initWindow() {
- // create a window frame and set the title in the toolbar
- JFrame window = new JFrame("Testing");
- // when we close the window, stop the app
- window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- // create the jpanel to draw on.
- // this also initializes the game loop
- initObjects();
- Screen screen = new Screen(mainCollection);
- // add the jpanel to the window
- window.add(screen);
- window.addKeyListener(screen);
- window.addMouseListener(screen);
- window.addMouseMotionListener(screen);
- // don't allow the user to resize the window
- window.setResizable(false);
- // fit the window size around the components (just our jpanel).
- // pack() should be called after setResizable() to avoid issues on some platforms
- window.pack();
- // open window in the center of the screen
- window.setLocationRelativeTo(null);
- // display the window
- window.setVisible(true);
- }
-
- public static void main(String[] args) {
- // invokeLater() is used here to prevent our graphics processing from
- // blocking the GUI. https://stackoverflow.com/a/22534931/4655368
- // this is a lot of boilerplate code that you shouldn't be too concerned about.
- // just know that when main runs it will call initWindow() once.
- SwingUtilities.invokeLater(App::initWindow);
- }
-
- public static void initObjects(){
- mainCollection = new ObjectCollection();
-
- BufferedImage testTexture;
- try {
- //testTexture = ImageIO.read(new File("/home/cory/Screenshot from 2022-06-06 18-52-12.png"));
- testTexture = ImageIO.read(new File("/home/cory/Screenshot from 2022-09-26 13-05-40.png"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- mainCollection.addObject(new Object3d(new PointComp[]{
- new PointComp(-30,-10,-10),
- new PointComp(-30,-10,10),
- new PointComp(-30,10,-10),
- new PointComp(-30,10,10),
- new PointComp(-10,-10,-10),
- new PointComp(-10,-10,10),
- new PointComp(-10,10,-10),
- new PointComp(-10,10,10)
-
- }, new int[][]{
- {0,2,3,1},
- {4,5,7,6},
- {0,1,5,4},
- {1,3,7,5},
- {3,2,6,7},
- {2,0,4,6},
- }, new Point2D[][]{
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
- },
- true,
- testTexture,
- true));
-// mainCollection.addObject(new Object3d(new PointComp[]{
-// new PointComp(-10,10,10),
-// new PointComp(-10,-10,10),
-// new PointComp(10,10,10),
-// new PointComp(10,-10,10),
-// new PointComp(0,0,24)
-// }, new int[][]{
-// {0,2,3,1},
-// {1,0,4},
-// {0,2,4},
-// {2,3,4},
-// {3,1,4}
-// }, true));
- mainCollection.addObject(new Object3d(new PointComp[]{
- new PointComp(0,0,0),
- new PointComp(20,0,0),
- new PointComp(0,10,0),
- new PointComp(0,0,10),
- }, new int[][]{
- {0,1,2},
- {0,2,3},
- {0,3,1},
- {2,1,3}
- }, new Point2D[][]{
- {new Point2D(0,0), new Point2D(100,0), new Point2D(0,100)},
- {new Point2D(0,0), new Point2D(100,0), new Point2D(0,100)},
- {new Point2D(0,0), new Point2D(100,0), new Point2D(0,100)},
- },
- true,
- testTexture,
- true));
- }
-}
diff --git a/src/main/java/Face.java b/src/main/java/Face.java
deleted file mode 100644
index d14427b..0000000
--- a/src/main/java/Face.java
+++ /dev/null
@@ -1,273 +0,0 @@
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-
-public class Face {
- public PointComp[] points;
- public Point2D[] UVPoints;
- public Vector3D normal;
- public Triangle[] tris;
- public boolean hasEdges;
- public BufferedImage texture;
- public Matrix[] perspectiveMappingMatrices = new Matrix[]{null, null, null, null, null, null};
- public boolean isInitialised;
- // fixed face
- public Face fixedFace;
- // working variables
- private final Vector3D traVec = new Vector3D(0,0, 0);
- // private final Vector2D scaVec = new Vector2D(0,0);
-
- private final Vector3D real01Vec = new Vector3D(0,0,0);
- private final Vector2D UV01Vec = new Vector2D(0,0);
- private final Vector3D real02Vec = new Vector3D(0,0,0);
- private final Vector2D UV02Vec = new Vector2D(0,0);
- double[] result;
- public void initialise(){
- calculateNormal();
- separateTris();
- // fixedFace is a constant object which is used temporarily when the face intersects with the camera,
- // and thus needs to be sliced.
- fixedFace = new Face();
- fixedFace.hasEdges = hasEdges;
- fixedFace.texture = texture;
- fixedFace.normal = normal;
- fixedFace.isInitialised = true;
- // the fixed face inherits the perspective mapping matrices from the true face. This is because the transforms
- // stay the same, just the edge bounds of the face have changed.
- fixedFace.perspectiveMappingMatrices = perspectiveMappingMatrices;
- isInitialised = true;
- }
-
- public void invalidate(){
- for (Triangle tri:
- tris) {
- tri.invalidate();
- }
- }
- public int draw(BufferedImage img, BufferedImage zBuf, BufferedImage debugImg, Matrix camMatrix, double FPDis, int scrX, int scrY){
- if(!isInitialised){
- throw new RuntimeException("Face not initialised");
- }
- // check for backface culling has been done previously.
- // initialise points
- int numberOfPixels = 0;
- boolean valid = applyPointTransforms(camMatrix, FPDis, scrX, scrY);
- //bakePerspectiveMatrices();
-
- if (valid) {
- 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];
- boolean lastValid = lastPoint.getRotatedPoint().z > 0.1;
- boolean thisValid;
- for (PointComp point:
- points) {
- thisValid = point.getRotatedPoint().z > 0.1;
- // We need to do different things depending on whether the previous point was also a valid point, or not.
- // first - if only 1 of the last point or this point were valid,
- // interpolate between them to get the point at the screen. (XOR)
- if(lastValid ^ thisValid){
- // solving for z = 0.1 for the line between thisPoint and lastPoint,
- // separately in the xz and yz planes.
- double gradX = (point.getRotatedPoint().z - lastPoint.getRotatedPoint().z) /
- (point.getRotatedPoint().x - lastPoint.getRotatedPoint().x);
- double gradY = (point.getRotatedPoint().z - lastPoint.getRotatedPoint().z) /
- (point.getRotatedPoint().y - lastPoint.getRotatedPoint().y);
-
- newPoints.add(new PointComp(
- (0.1+gradX*point.getRotatedPoint().x-point.getRotatedPoint().z)/gradX,
- (0.1+gradY*point.getRotatedPoint().y-point.getRotatedPoint().z)/gradY,
- 0.1));
- if(!Double.isFinite(gradX)){
- newPoints.get(newPoints.size() - 1).point.x = point.getRotatedPoint().x;}
- if(!Double.isFinite(gradY)){
- newPoints.get(newPoints.size() - 1).point.y = point.getRotatedPoint().y;}
- }
- // finally - if the current point is valid, then add it to the list
- if(thisValid){
- newPoints.add(new PointComp(
- point.getRotatedPoint().x,
- point.getRotatedPoint().y,
- point.getRotatedPoint().z));
- }
- lastPoint = point;
- lastValid = thisValid;
- }
- // there must be at least 3 points in the face for it to be drawn successfully
- if(newPoints.size() >= 3) {
- // finished fixing points, now we need to create a new face consisting of those points.
- fixedFace.points = newPoints.toArray(new PointComp[0]);
- fixedFace.separateTris();
- // invalidate all the points so they are actually calculated
- for (PointComp point :
- newPoints) {
- point.invalidate();
- }
- Matrix identMat = new Matrix(3,3);
- // we use an identity matrix because the points of the fixed face are in camera coordinates already.
- // we just need the projected 2d point.
- identMat.setItems(new double[][]{
- {1,0,0},
- {0,1,0},
- {0,0,1}
- });
- fixedFace.applyPointTransforms(identMat, FPDis, scrX, scrY);
- fixedFace.drawTris(img, zBuf, FPDis, scrX, scrY);
- }
- }
- 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:
- points) {
- 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);}
- }
- return valid;
- }
- public void separateTris(){
- Triangle[] newTris = new Triangle[points.length - 2];
- for(int i = 0; i< newTris.length; i+=1){
- newTris[i] = new Triangle(
- points[0].getProjectedPoint(),
- points[i+1].getProjectedPoint(),
- points[i+2].getProjectedPoint(),
- new boolean[]{hasEdges, hasEdges, hasEdges},
- texture, perspectiveMappingMatrices[i]);
- }
- tris = newTris;
- }
- public void calculateNormal(){
- // too many new variables
- Point3D point0 = points[0].point;
- Point3D point1 = points[1].point;
- Point3D point2;
- Vector3D vec1 = new Vector3D(point1.x - point0.x, point1.y - point0.y, point1.z - point0.z);
- Vector3D vec2 = new Vector3D(0,0,0); // initialisation otherwise intellij gets mad
- // find a vector which is not inline with other vectors
- boolean valid = false; int i = 2;
- while(!valid && i < points.length) {
- point2 = points[i].point;
- vec2 = new Vector3D(point2.x - point0.x, point2.y - point0.y, point2.z - point0.z);
- double angle = Math.abs(vec1.angleTo(vec2));
- if(angle > 0.1 && angle < 2*Math.PI - 0.1){
- // if the angle between the vectors is between a threshold, the two vectors are valid.
- // else, calculate the second vector using a different set of points.
- valid = true;
- }}
- if(!valid){throw new RuntimeException("Could not calculate normal of face");}
- normal = vec1.cross(vec2);
- }
-// private void bakePerspectiveMatrices() {
-// // one mapping matrix for each triangle
-// // to achieve perspective mapping, we need to convert from the 2d screen position, to the 3d world position by
-// // reverse projecting and interpolating in the triangle - this gives camera coordinates.
-// // next, we need to use the inverse camera matrix to convert from camera coordinates to world coordinates.
-// // then, we can use the perspective mapping matrix unique and baked to each triangle on each face to convert from those world coordinates, into uv coordinates.
-//
-//
-//
-// real01Vec.createFrom2Points(points[0].getRotatedPoint(), points[1].getRotatedPoint());
-// real02Vec.createFrom2Points(points[0].getRotatedPoint(), points[2].getRotatedPoint());
-// // scaVec.createFrom2Points(points[0].getProjectedPoint(), points[1].getProjectedPoint());
-// UV01Vec.createFrom2Points(UVPoints[0], UVPoints[1]);
-// UV02Vec.createFrom2Points(UVPoints[0], UVPoints[2]);
-// // it must remain as the same object so pointers elsewhere still work.
-// // invert x and y coordinates because in rotated coordinates, they are the wrong way round.
-// perspectiveMappingMatrix.setItems(new double[][]{
-// {0,1,0,0},
-// {1,0,0,0},
-// {0,0,1,0},
-// {0,0,0,1},
-// });
-//
-// traVec.createFrom2Points(new Point3D(UVPoints[0].y, UVPoints[0].x, 0), points[0].getRotatedPoint());
-// Matrix tMat = new Matrix(4, 4);
-//// tMat.setItems(new double[][]{
-//// {1, 0, 0, traVec.x},
-//// {0, 1, 0, traVec.y},
-//// {0, 0, 1, traVec.z},
-//// {0, 0, 0, 1},
-//// });
-// tMat.setItems(new double[][]{
-// {1, 0, 0, 0},
-// {0, 1, 0, 0},
-// {0, 0, 1, points[0].getRotatedPoint().z},
-// {0, 0, 0, 1},
-// });
-// double scale = 0.1;//(real01Vec.getLength() / UV01Vec.getLength());
-// Matrix scaMat = new Matrix(4, 4);
-// scaMat.setItems(new double[][]{
-// {scale, 0, 0, 0},
-// {0, scale, 0, 0},
-// {0, 0, scale, 0},
-// {0, 0, 0, 1}
-// });
-// // find z rotation and define matrix
-// double zAng = new Vector2D(real01Vec.x, real01Vec.y).angleTo(UV01Vec);
-// Matrix zMat = new Matrix(4, 4);
-// zMat.setItems(new double[][]{
-// {Math.cos(zAng), Math.sin(zAng), 0, 0},
-// {-Math.sin(zAng), Math.cos(zAng), 0, 0},
-// {0, 0, 1, 0},
-// {0, 0, 0, 1}}
-// );
-// // rotate "real" vectors using the Z matrix
-// result = zMat.multiplyPoint3raw(real01Vec.x, real01Vec.y, real01Vec.z);
-// real01Vec.x = result[0];real01Vec.y = result[1];real01Vec.z = result[2];
-// result = zMat.multiplyPoint3raw(real02Vec.x, real02Vec.y, real02Vec.z);
-// real02Vec.x = result[0];real02Vec.y = result[1];real02Vec.z = result[2];
-// // invert the Z matrix (todo cleanup)
-// zMat.setItems(new double[][]{
-// {Math.cos(-zAng), Math.sin(-zAng), 0, 0},
-// {-Math.sin(-zAng), Math.cos(-zAng), 0, 0},
-// {0, 0, 1, 0},
-// {0, 0, 0, 1}}
-// );
-// // find Y rotation and define matrix
-// double yAng = new Vector2D(real01Vec.x, real01Vec.z).angleTo(new Vector2D(UV01Vec.x, 0));
-// Matrix yMat = new Matrix(4, 4);
-// yMat.setItems(new double[][]{
-// {Math.cos(yAng), 0, -Math.sin(yAng), 0},
-// {0, 1, 0, 0},
-// {Math.sin(yAng), 0, Math.cos(yAng), 0},
-// {0, 0, 0, 1}}
-// );
-// result = yMat.multiplyPoint3raw(real02Vec.x, real02Vec.y, real02Vec.z);
-// real02Vec.x = result[0];real02Vec.y = result[1];real02Vec.z = result[2];
-// yMat.setItems(new double[][]{
-// {Math.cos(-yAng), 0, -Math.sin(-yAng), 0},
-// {0, 1, 0, 0},
-// {Math.sin(-yAng), 0, Math.cos(-yAng), 0},
-// {0, 0, 0, 1}}
-// );
-// double xAng = new Vector2D(real02Vec.y, real02Vec.z).angleTo(new Vector2D(UV01Vec.x, 0)); // this is fine
-// Matrix xMat = new Matrix(4, 4);
-// xMat.setItems(new double[][]{
-// {1, 0, 0, 0},
-// {0, Math.cos(-xAng), Math.sin(-xAng), 0},
-// {0, -Math.sin(-xAng), Math.cos(-xAng), 0},
-// {0, 0, 0, 1}}
-// );
-// // Matrix that returns the position on a texture from 3d camera space on the face of an object
-// // we already know the Z depth because of calculations for Z buffers.
-// //perspectiveMappingMatrix.multiply(scaMat);
-// perspectiveMappingMatrix.multiply(scaMat.multiplyGetResult(tMat));
-// //perspectiveMappingMatrix.multiply((scaMat.multiplyGetResult(xMat.multiplyGetResult(yMat.multiplyGetResult(zMat.multiplyGetResult(tMat))))));
-// // perspectiveMappingMatrix.multiply(scaMat.multiplyGetResult(zMat.multiplyGetResult(yMat.multiplyGetResult(xMat.multiplyGetResult(tMat)))));
-// }
-}
diff --git a/src/main/java/JsonWriter.java b/src/main/java/JsonWriter.java
deleted file mode 100644
index faeaf33..0000000
--- a/src/main/java/JsonWriter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-import org.codehaus.jackson.map.ObjectMapper;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-public class JsonWriter {
-
- public static void main(String[] args) {
-
- Country countryObj = new Country();
- countryObj.name = "India";
- countryObj.population = 1000000;
-
- List listOfStates = new ArrayList();
- listOfStates.add("Madhya Pradesh");
- listOfStates.add("Maharastra");
- listOfStates.add("Rajasthan");
-
- countryObj.states = listOfStates ;
- ObjectMapper mapper = new ObjectMapper();
-
- try {
-
- // Writing to a file
- mapper.writeValue(new File("c:\\country.json"), countryObj );
-
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/main/java/Line2d.java b/src/main/java/Line2d.java
deleted file mode 100644
index 3e03428..0000000
--- a/src/main/java/Line2d.java
+++ /dev/null
@@ -1,138 +0,0 @@
-import java.awt.*;
-import java.awt.image.BufferedImage;
-
-// handles line and line drawing
-public class Line2d {
- public Point2D point1;
- public Point2D point2;
-
- public boolean isDrawn;
-
- // initializer variables
- boolean is_initialised = false;
- private Point2D realPoint1; private Point2D realPoint2;
- private int dx;
- private int dy; private int sDy;
- private char iterator;
- private Point2D xy;
- int[] returnVal = new int[]{0,0};
- private double gradient; // z gradient of the line in terms of X
-
- // drawing progress variables
- private int iteratorVal;
- private int D;
- public Line2d(Point2D _point1, Point2D _point2, boolean _isDrawn){
- point1 = _point1;
- point2 = _point2;
- isDrawn = _isDrawn;
- }
-
- /**
- * Draws onto a given image
- * @param img the image to draw onto
- */
- public void draw(BufferedImage img){
- if (!is_initialised){initialise();}
- if (iterator == 'x'){
- for (int i = 0; i <= dx; i++){
- nextPix();
- 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++){
- nextPix();
- 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(.5f, 1, 1));
- }
- }
- }
- is_initialised = false;
- /*
- debug - draws the start and end of each line in white and green
- img.setRGB(realPoint1.intX(), realPoint1.intY(), Color.blue.getRGB());
- img.setRGB(realPoint2.intX(), realPoint2.intY(), Color.green.getRGB());
- */
- }
-
- /**
- * Performs the initial calculations required to draw the line
- * Is automatically called whenever nextPix() or draw() are called when initialise() has not run.
- */
- public void initialise(){
- // initialise brensenham algorithm
- if (point2.x > point1.x) {
- realPoint1 = point1;
- realPoint2 = point2;
- } else { // if dx is less than zero, swap the points around
- realPoint1 = point2;
- realPoint2 = point1;
- }
- dx = realPoint2.x - realPoint1.x;
- dy = realPoint2.y - realPoint1.y;
- sDy = (int) Math.signum(dy); dy = Math.abs(dy);
-
- xy = new Point2D(realPoint1.x,realPoint1.y); // starting point
-
- // check if dy is greater than or less than dx
- if (dy < dx){
- iterator = 'x';
- D = (2*dy) - dx;
- }else{
- iterator='y';
- D = (2*dx) - dy;
- }
- iteratorVal = 0;
-
- // init other variables
- // dz / dx
- gradient = (realPoint2.z-realPoint1.z) /
- (realPoint2.x - realPoint1.x);
- is_initialised = true;
- }
-
- /**
- * @return the x and y coordinate of the next pixel in the line.
- */
- public int[] nextPix(){
- if(!is_initialised){initialise();}
- returnVal[0] = xy.x;
- returnVal[1] = xy.y;
- if (iterator=='x' && iteratorVal < dx && iteratorVal != -1){
- if (D > 0) {
- D += 2 * (dy - dx);
- xy.y += sDy;
- } else {
- D += 2*dy;
- }
- xy.x += 1; // the line is always drawn left to right
- } else if(iterator =='y' && iteratorVal < dy && iteratorVal != -1) {
- if (D > 0) {
- D += 2 * (dx-dy);
- xy.x += 1; // the line is always drawn left to right
- } else {
- D += 2*dx;
- }
- xy.y += sDy;
-
- }
- else if(iteratorVal != -1) {
- iteratorVal = -1;
- returnVal[0] = realPoint2.x;
- returnVal[1] = realPoint2.y;
- return returnVal;
- }
- else {
- is_initialised = false;
- throw new RuntimeException("Accessed too many line pixels");
- }
- iteratorVal += 1;
- return returnVal;
- }
- public double getZVal(int x){
- return realPoint1.z + (gradient * (x - realPoint1.x));
- }
- }
-
diff --git a/src/main/java/Matrix.java b/src/main/java/Matrix.java
deleted file mode 100644
index c63ac19..0000000
--- a/src/main/java/Matrix.java
+++ /dev/null
@@ -1,100 +0,0 @@
-public class Matrix {
- protected int x;
- protected int y;
- private double[][] items;
-
- public Matrix(int _x, int _y){
- x = _x; y= _y;
- items = new double[y][x];
- }
- public double getItem(int _x, int _y){
- return items[_y][_x];
- }
- public void setItem(int _x, int _y, double val){
- items[_y][_x] = val;
- }
- public double[][] getItems() {
- return items;
- }
- public void setItems(double[][] newItems){
- items = newItems;
- }
- public Matrix multiplyGetResult(Matrix multiplier) {
- Matrix result = new Matrix(multiplier.x, this.y);
- 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= distance){
- pointA = points[i].point;
- distance = newDis;}}
- for (PointComp point : points) {
- newDis = Math.pow(pointA.x - point.point.x, 2) +
- Math.pow(pointA.y - point.point.y, 2) +
- Math.pow(pointA.z - point.point.z, 2);
- if (newDis >= distance){
- pointB = point.point;
- distance = newDis;}}
- boundingSphereC = new Point3D(
- (pointA.x + pointB.x) / 2,
- (pointA.y + pointB.y)/2,
- (pointA.z + pointB.z)/2);
- boundingSphereR = Math.sqrt(distance)/2;
- for (PointComp point:
- points) {
- distance = Math.sqrt(
- Math.pow(boundingSphereC.x - point.point.x, 2)+
- Math.pow(boundingSphereC.y - point.point.y, 2)+
- Math.pow(boundingSphereC.z - point.point.z, 2));
- if(distance > boundingSphereR){
- boundingSphereR = distance;
- }
- }
- }
-}
diff --git a/src/main/java/ObjectCollection.java b/src/main/java/ObjectCollection.java
deleted file mode 100644
index db9bf6c..0000000
--- a/src/main/java/ObjectCollection.java
+++ /dev/null
@@ -1,129 +0,0 @@
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-import java.util.Objects;
-
-// stores both objects and other object collections
-public class ObjectCollection {
- public ArrayList points = new ArrayList();
- private Object3d collectionObject;
- public double boundingSphereR;
- public Point3D boundingSphereC;
-
-
- public ArrayList subCollections = new ArrayList();
- public ArrayList objects = new ArrayList();
-
- public void invalidate(boolean invalidatePoints){
- // the first level of object collections should contain all the points for the sublevels.
- // this means that we only need to invalidate them at the top level
- if(invalidatePoints){for (PointComp point:
- points) {
- point.invalidate();
- }}
- for(ObjectCollection subCollection:
- subCollections){
- subCollection.invalidate(false);
- }
- for(Object3d object:
- objects){
- object.invalidate();
- }
- }
-
- public void draw(BufferedImage img, BufferedImage zBuf, BufferedImage debugImg, Matrix camMatrix, Point3D playerPos, Plane[] frustumPlanes, double FPDis, int scrX, int scrY){
- for (Object3d object:
- objects) {
- boolean draw = true;
- int i = 0;
- for (Plane plane:
- frustumPlanes) {
- 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);}
- }
- // todo check for frustum culling
- for(ObjectCollection collection:
- subCollections){
- boolean draw = true;
- int i = 0;
- for (Plane plane:
- frustumPlanes) {
- 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);
- }
- }
- public void addCollection(int[] pointList){
- subCollections.add(new ObjectCollection());
- }
- // making sure that all the point indices make sense might be a nightmare but ehh
- public void addObject(Object3d object){
- PointComp[] newpoints = object.points;
- objects.add(object);
- // add the new object
-
- // merge lists
- for (PointComp newpoint : newpoints) {
- boolean found = false;
- // find out if any of the new points already exist in the list
- for (PointComp point : points) {
- if (newpoint == point) {
- found = true;
- break;
- }
- }
- if (!found) {
- points.add(newpoint);
- }
- }
- }
- public void initialise(){
- // init bounding sphere
- double distance = 0;
- double newDis;
- Point3D pointA = points.get(0).point;
- Point3D pointB = points.get(0).point;
- // todo - maybe use some vector classes?
- for (int i = 1; i < points.size(); i+=1) {
- newDis = Math.pow(points.get(0).point.x - points.get(i).point.x, 2) +
- Math.pow(points.get(0).point.y - points.get(i).point.y, 2) +
- Math.pow(points.get(0).point.z - points.get(i).point.z, 2);
- if (newDis >= distance){
- pointA = points.get(i).point;
- distance = newDis;}}
- for (PointComp point : points) {
- newDis = Math.pow(pointA.x - point.point.x, 2) +
- Math.pow(pointA.y - point.point.y, 2) +
- Math.pow(pointA.z - point.point.z, 2);
- if (newDis >= distance){
- pointB = point.point;
- distance = newDis;}}
- boundingSphereC = new Point3D(
- (pointA.x + pointB.x) / 2,
- (pointA.y + pointB.y)/2,
- (pointA.z + pointB.z)/2);
- boundingSphereR = Math.sqrt(distance)/2;
- boolean valid = false;
- for (PointComp point:
- points) {
- distance = Math.sqrt(
- Math.pow(boundingSphereC.x - point.point.x, 2)+
- Math.pow(boundingSphereC.y - point.point.y, 2)+
- Math.pow(boundingSphereC.z - point.point.z, 2));
- if(distance > boundingSphereR){
- boundingSphereR = distance;
- }
- }
- }
-}
diff --git a/src/main/java/Plane.java b/src/main/java/Plane.java
deleted file mode 100644
index fa525cf..0000000
--- a/src/main/java/Plane.java
+++ /dev/null
@@ -1,27 +0,0 @@
-public class Plane {
- public Point3D mainPoint;
- public Vector3D normalVector;
-
- public double getDistance(Point3D point){
- Vector3D vec = new Vector3D(0,0,0);
- vec.createFrom2Points(mainPoint, point);
- return normalVector.dot(vec);
- }
- public void initFrom3Points(Point3D _mainPoint, Point3D point1, Point3D point2){
- // find normal vector to 3 points
- Vector3D vec1 = new Vector3D(0,0,0);
- Vector3D vec2 = new Vector3D(0,0,0);
- vec1.createFrom2Points(_mainPoint, point1);
- vec2.createFrom2Points(point1, point2);
- initFromPointAndVector(_mainPoint, vec1.cross(vec2));
- }
- public void initFromPointAndVector(Point3D _mainPoint, Vector3D _normalVector){
- // convert to unit vector
- mainPoint = _mainPoint;
- double len = _normalVector.getLength();
- normalVector = new Vector3D(
- _normalVector.x / len,
- _normalVector.y / len,
- _normalVector.z / len);
- }
-}
diff --git a/src/main/java/Player.java b/src/main/java/Player.java
deleted file mode 100644
index 9a9d682..0000000
--- a/src/main/java/Player.java
+++ /dev/null
@@ -1,223 +0,0 @@
-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
- private BufferedImage image;
- // 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 Point3D position = new Point3D(0,0,0);
- private Point3D rotation = new Point3D(0,Math.PI / 2, 0);
- private Point3D direction = new Point3D(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 {
- Point3D dirvec = new Point3D(0,0,0);
- izMat.multiplyPoint3to(direction, dirvec);
- 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);
- // todo - work out why i need to reverse this
- 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].initFromPointAndVector(position, viewVector);
- 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;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/Point2D.java b/src/main/java/Point2D.java
deleted file mode 100644
index e203550..0000000
--- a/src/main/java/Point2D.java
+++ /dev/null
@@ -1,20 +0,0 @@
-import java.util.ArrayList;
-
-public class Point2D {
- public int x;
- public int y;
- public double z;
- // contains a Z value so that projected points can have their Z values calculated efficiently
- public Point2D(int _x, int _y){
- x = _x;
- y = _y;
- }
- public void set(int _x, int _y){
- x = _x;
- y = _y;
- }
- public int[] get(){
- return new int[]{x, y};
- }
-
-}
diff --git a/src/main/java/Point3D.java b/src/main/java/Point3D.java
deleted file mode 100644
index e6a51cf..0000000
--- a/src/main/java/Point3D.java
+++ /dev/null
@@ -1,36 +0,0 @@
-public class Point3D {
- public double x;
- public double y;
- public double z;
-
- public Point3D(double _x, double _y, double _z){
- x = _x;
- y = _y;
- z = _z;
- }
- public void set(double[] _new){
- x = _new[0];
- y = _new[1];
- z = _new[2];
- }
- public void translate(Point3D trVec){
- x += trVec.x;
- y += trVec.y;
- z += trVec.z;
- }
- public Point2D project(double fpdis, int scrX, int scrY){
- return new Point2D(
- scrX - (int)(scrX*0.5*((fpdis*y)/(z) + 1)),
- (int)(scrX*0.5*((fpdis*x)/(z) + ((double)scrY/(double)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
- point.x = scrX - (int)(scrX*0.5*((fpdis*y)/(z) + 1));
- point.y = (int)(scrX*0.5*((fpdis*x)/(z) + ((double)scrY/(double)scrX)));
- }
- public double[] get(){
- return new double[]{x, y, z};
- }
-}
diff --git a/src/main/java/PointComp.java b/src/main/java/PointComp.java
deleted file mode 100644
index 730ef6b..0000000
--- a/src/main/java/PointComp.java
+++ /dev/null
@@ -1,40 +0,0 @@
-public class PointComp {
- public Point3D point = new Point3D(0,0,0);
- private final Point3D rotatedPoint = new Point3D(0,0,0);
- private boolean isRotatedPointValid = true;
- private final Point2D projectedPoint = new Point2D(0,0);
- private boolean isProjectedPointValid = true;
- public PointComp(double _x, double _y, double _z){
- point.x = _x;
- point.y = _y;
- point.z = _z;
- }
- public void invalidate(){
- isProjectedPointValid = false; isRotatedPointValid = false;
- }
- public Point3D getRotatedPoint(){
- if(!isRotatedPointValid){
- throw new RuntimeException("Rotated point not initialised");
- }
- return rotatedPoint;
- }
- public Point2D getProjectedPoint(){
- if(!isRotatedPointValid){
- throw new RuntimeException("Projected point not initialised");
- }
- return projectedPoint;
- }
- public void setRotatedPoint(Matrix camMatrix){
- if(!isRotatedPointValid) {
- camMatrix.multiplyPoint3to(point, rotatedPoint);
- isRotatedPointValid = true;
- }
- }
- public void setProjectedPoint(double FPDis, int scrX, int scrY){
- if(!isProjectedPointValid) {
- rotatedPoint.projectOn(FPDis, scrX, scrY, projectedPoint);
- projectedPoint.z = rotatedPoint.z;
- isProjectedPointValid = true;
- }
- }
-}
diff --git a/src/main/java/Screen.java b/src/main/java/Screen.java
deleted file mode 100644
index 8034e4c..0000000
--- a/src/main/java/Screen.java
+++ /dev/null
@@ -1,286 +0,0 @@
-import com.zetcode.CSV;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.BufferedImage;
-
-public class Screen extends JPanel implements ActionListener, KeyListener, MouseListener, MouseMotionListener {
- // __working variables__
- public long lastTime = 0;
- // mouse
- private boolean captured = false;
- private final Point2D mouseRel = new Point2D(0,0);
- private final Cursor invisibleCursor;
-
- public ObjectCollection mainCollection;
- public CSV csv;
-
- // testing\
-// private Line2d line = new Line2d(
-// new Point2D(200, 200),
-// new Point2D(1, 1),
-// true);
-// private Triangle Triangle = new Triangle(
-// new Point2D(200, 200), new Point2D(1, 1), new Point2D(400,200)
-// ,new boolean[]{false, false, false},
-// null,
-// new Matrix(3, 3));
-// private final Point3D[] points = new Point3D[]{
-// new Point3D(10,10,-1),
-// new Point3D(-10, 10, -1),
-// new Point3D(-10, -10, -1),
-// new Point3D(10, -10, -1)};
- // double ang = 0;
- // __config variables__
- // controls the delay between each tick in ms
- private final int DELAY = 25;
- // controls the size of the board
- public static final int TILE_SIZE = 50;
- public static final int ROWS = 12;
- public static final int COLUMNS = 18;
- // suppress serialization warning
- private static final long serialVersionUID = 490905409104883233L;
-
- // keep a reference to the timer object that triggers actionPerformed() in
- // case we need access to it in another method
- private final Timer timer;
- private final Player player;
-
- public Screen(ObjectCollection _mainCollection) {
- mainCollection = _mainCollection;
-
- // set the game board size
- setPreferredSize(new Dimension(TILE_SIZE * COLUMNS, TILE_SIZE * ROWS));
- // 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)
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Point hotSpot = new Point(0,0);
- BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT);
- invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor");
-
- // initialize the game state
- player = new Player(TILE_SIZE*COLUMNS, TILE_SIZE*ROWS);
- // this timer will call the actionPerformed() method every DELAY ms
- timer = new Timer(DELAY, this);
- timer.start();
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- // this method is called by the timer every DELAY ms.
- // runs 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.
- repaint();
- }
-
- @Override
- public void paintComponent(Graphics g) {
- super.paintComponent(g);
- // when calling g.drawImage() we can use "this" for the ImageObserver
- // because Component implements the ImageObserver interface, and JPanel
- // extends from Component. So "this" Board instance, as a Component, can
- // react to imageUpdate() events triggered by g.drawImage()
-
- // draw graphics.
- drawScreen(g);
- // player.draw(g, this);
-
- // this smooths out animations on some systems
- Toolkit.getDefaultToolkit().sync();
- }
- private void drawScreen(Graphics g) {
- BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB );
- BufferedImage zBuf = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
- BufferedImage debugImg = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
- debugImg.createGraphics();
- g.setColor(Color.white);
-
-// System.out.println(zBuf.getRGB(0,0));
-// zBuf.setRGB(0,0, 100);
-// System.out.println(zBuf.getRGB(0,0));
-
-// line.point2.x = 100*Math.sin(ang) + 200;
-// line.point2.y = 100*Math.cos(ang) + 200;
-// Triangle.point2.x = 100*Math.sin(ang+3) + 200;
-// Triangle.point2.y = 100*Math.cos(ang+3) + 200;
-// line.draw(img);
- /* ArrayList newPoints = new ArrayList();
- for (Point3D point: points) {
- Point3D _new = new Point3D(0,0,0);
- player.camMatrix.multiplyPoint3to(point, _new);
- if(_new.z > .1) {
- newPoints.add(_new);
- } else{
- newPoints.add(null);
- }
- }
- Triangle t1 = null;
- Triangle t2 = null;
- try {
- t1 = new Triangle(
- newPoints.get(0).project(player.FPDis, getWidth(), getHeight()),
- newPoints.get(1).project(player.FPDis, getWidth(), getHeight()),
- newPoints.get(2).project(player.FPDis, getWidth(), getHeight()));
- } catch(NullPointerException ignored){}
- try{
- t2 = new Triangle(
- newPoints.get(0).project(player.FPDis, getWidth(), getHeight()),
- newPoints.get(2).project(player.FPDis, getWidth(), getHeight()),
- newPoints.get(3).project(player.FPDis, getWidth(), getHeight()));
- } catch(NullPointerException ignored){}
-
- try{t1.draw(img);}catch (NullPointerException ignored){}
- try{t2.draw(img);}catch (NullPointerException ignored){}
- ang += 0.02;*/
-
- mainCollection.invalidate(true);
- mainCollection.draw(img, zBuf, debugImg, player.camMatrix, player.getPos(), player.frustumPlanes, player.Fpdis, getWidth(), getHeight());
- g.drawImage(img, 0, 0, this);
-
- // DEBUG DRAWING
- {
- debugImg.getGraphics().drawString(Math.round(1000 / (float) (System.currentTimeMillis() - lastTime)) + " fps", 10, 10);
- 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: " +
- String.format("%.2f", player.getPos().x) + " " +
- String.format("%.2f", player.getPos().y) + " " +
- String.format("%.2f", player.getPos().z) + " ", 100, 50);
- 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: " +
- 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 + ": " +
- 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);
- }
- // write debug overlay
- g.drawImage(debugImg, 0, 0, this);
- lastTime = System.currentTimeMillis();
- }
- // CSV WRITING
- // each two lines line in the file represents an object in blender.
- // They contain a list of vertices on even numbered lines, and a list of faces on odd numbered lines
- csv = new CSV("debugOUT.csv");
- String[] writingString = new String[]{};
- // camera object
- for (int i = 0; i < 4; i += 1) {
- String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().x),
- String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().y),
- String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().z);
- csv.writeLine();
- }
-
- @Override
- public void keyTyped(KeyEvent e) {}
-
- @Override
- public void keyPressed(KeyEvent e) {
- int key = e.getKeyCode();
-
- if (key == KeyEvent.VK_ESCAPE){
- captured = false;
- setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
- }
- // pass key-press to the player, so they can handle it as well
- player.keyPressed(key);
- }
-
- @Override
- public void keyReleased(KeyEvent e) {
- int key = e.getKeyCode();
-
- // pass key-release to the player, so they can handle it as well
- player.keyReleased(key);
- }
-
- // gets the relative position of the mouse since the last time this function was called
- public Point2D get_mouse_rel(){
- Point2D rel;
- // this doesn't work if the mouse isn't captured. If I need it to, I can
- // probably make it, but I don't know what the functionality looks like.
- if (captured) {
- rel = new Point2D(MouseInfo.getPointerInfo().getLocation().x - getLocationOnScreen().x - getSize().width / 2 + mouseRel.x,
- MouseInfo.getPointerInfo().getLocation().y - getLocationOnScreen().y - getSize().height / 2 + mouseRel.y);
- mouseRel.set(0, 0);
- // set the position of the mouse back to (0,0)
- try {
- Robot robot = new Robot();
- robot.mouseMove(getLocationOnScreen().x + getSize().width / 2,
- getLocationOnScreen().y + getSize().height / 2);
- } catch (AWTException ex) {
- ex.printStackTrace();
- }
- }
- else{
- rel = new Point2D(0,0);
- }
- return rel;
- }
- @Override
- public void mouseClicked(MouseEvent mouseEvent) {
- if (mouseEvent.getButton() == MouseEvent.BUTTON1){
- captured = true;
- get_mouse_rel();
- setCursor(invisibleCursor);
- }
- }
-
- @Override
- public void mousePressed(MouseEvent mouseEvent) {}
-
- @Override
- public void mouseReleased(MouseEvent mouseEvent) {}
-
- @Override
- public void mouseEntered(MouseEvent mouseEvent) {}
-
- @Override
- public void mouseExited(MouseEvent mouseEvent) {
- if (captured) {
- // add current mouse relative location to mouseRel so mouse movements are not lost
- mouseRel.x += mouseEvent.getX() - getSize().width / 2d;
- mouseRel.y += mouseEvent.getY() - getSize().height / 2d;
-
- // System.out.println(mouseEvent.getX() + " " + mouseEvent.getY());
- // set the position of the mouse back to (0,0)
- try {
- Robot robot = new Robot();
- robot.mouseMove(getLocationOnScreen().x + getSize().width / 2,
- getLocationOnScreen().y + getSize().height / 2);
- } catch (AWTException ex) {
- ex.printStackTrace();
- }
- }
- }
-
- @Override
- public void mouseDragged(MouseEvent mouseEvent) {}
-
- @Override
- public void mouseMoved(MouseEvent mouseEvent) {
- }
-}
\ No newline at end of file
diff --git a/src/main/java/Triangle.java b/src/main/java/Triangle.java
deleted file mode 100644
index ff12683..0000000
--- a/src/main/java/Triangle.java
+++ /dev/null
@@ -1,178 +0,0 @@
-import org.omg.CORBA.PolicyTypeHelper;
-
-import java.awt.*;
-import java.awt.image.BufferedImage;
-
-public class Triangle{
- public Point2D point1;
- public Point2D point2;
- public Point2D point3;
-
- public Matrix perspectiveMappingMatrix;
-
-
- public boolean[] edgeList; //edge 1-2 , 2-3, 3-1
-
- private Line2d LineLong;
- private Line2d LineA;
- private Line2d LineB;
-
- public BufferedImage texture;
-
- // initialisation variables
- private boolean is_initialised = false;
- private Point2D min;
- private Point2D max;
- // progress variables
- private Point3D result = new Point3D(0,0,0);
- private Point2D result2 = new Point2D(0,0);
- public void invalidate() {
- is_initialised = false;
- }
- public Triangle(Point2D _pA, Point2D _pB, Point2D _pC, boolean[] _edgeList, BufferedImage _texture, Matrix mapMatrix){
- point1 = _pA;
- point2 = _pB;
- point3 = _pC;
- edgeList = _edgeList;
- texture = _texture;
- perspectiveMappingMatrix = mapMatrix;
- }
- // returns int for debug
- public int draw(BufferedImage img, BufferedImage zBuf, double FPDis, int scrX, int scrY){
- //
- long lastMillis;
-
-
- if (!is_initialised){initialise();}
- int[] point1;
- int[] point2;
- char currentLine = 'A';
-
- lastMillis = System.currentTimeMillis();
-
- point1 = LineLong.nextPix();
- point2 = LineA.nextPix();
- 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] < img.getWidth() && point1[1] < img.getHeight()){
- img.setRGB(point1[0], point1[1], Color.HSBtoRGB(0, 1, 1));
- }
- try { // this error seems to be thrown randomly, for various reasons // todo fix
- point1 = LineLong.nextPix();
- }
- catch (Exception e){
- throw new RuntimeException("accessed too many line pixels");
- }
- }
- while(x-1 == point2[0]) {
- if (currentLine == 'A') {
- try{
- if(LineA.isDrawn && // draw line pixels if needed, and on screen
- point2[0] > 0 && point2[1] > 0 && point2[0] < img.getWidth() && point2[1] < img.getHeight()){
- img.setRGB(point2[0], point2[1], Color.HSBtoRGB(0f, 1, 1));
- }
- point2 = LineA.nextPix();
- }
- catch (RuntimeException e){
- currentLine = 'B';
- // point2 = LineB.nextPix();
- }
- }
- else {
- if(LineB.isDrawn && // draw line pixels if needed, and on screen
- point2[0] > 0 && point2[1] > 0 && point2[0] < img.getWidth() && point2[1] < img.getHeight()){
- img.setRGB(point2[0], point2[1], Color.HSBtoRGB(0f, 1, 1));
- }
- point2 = LineB.nextPix();
- }
- }
- // cancel drawing if the x value of the triangle is out of bounds
- if (x >= img.getWidth()) {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], img.getHeight() - 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]);
- }
- } else {
- for (int y = Math.max(point2[1], 0); y <= Math.min(point1[1], img.getHeight() - 1); y += 1) {
- drawPix(img, zBuf, FPDis, scrX, scrY, currentLine, x,y, point1[1], point2[1]);
- }
- }
- }
- }
- lastMillis = (System.currentTimeMillis() - lastMillis);
- return (int)lastMillis;
- }
- public void initialise(){
- if (point1 == null || point2 == null || point3 == null){
- throw new NullPointerException();
- }
- min = new Point2D(Math.min(point1.x, Math.min(point2.x, point3.x)), Math.min(point1.y, Math.min(point2.y, point3.y)));
- max = new Point2D(Math.max(point1.x, Math.max(point2.x, point3.x)), Math.max(point1.y, Math.max(point2.y, point3.y)));
- // woo horrible IFs mess.
- // we need to figure out which points touch the edges in order to find which line is the 'full length' edge,
- // and then assign line A and line B in order
- if (point1.x == min.x) {
- if (point2.x == max.x){
- LineLong = new Line2d(point1, point2, edgeList[0]);
- LineA = new Line2d(point1, point3, edgeList[2]);
- LineB = new Line2d(point3, point2, edgeList[1]);
- } else {
- LineLong = new Line2d(point1, point3, edgeList[2]);
- LineA = new Line2d(point1, point2, edgeList[0]);
- LineB = new Line2d(point2, point3, edgeList[1]);
- }
- }
- else if (point2.x == min.x) {
- if (point1.x == max.x) {
- LineLong = new Line2d(point2, point1, edgeList[0]);
- LineA = new Line2d(point2, point3, edgeList[1]);
- LineB = new Line2d(point3, point1, edgeList[2]);
- } else {
- LineLong = new Line2d(point2, point3, edgeList[1]);
- LineA = new Line2d(point2, point1, edgeList[0]);
- LineB = new Line2d(point1, point3, edgeList[2]);
- }
- }
- else if (point3.x == min.x){
- if (point1.x == max.x) {
- LineLong = new Line2d(point3, point1, edgeList[2]);
- LineA = new Line2d(point3, point2, edgeList[1]);
- LineB = new Line2d(point2, point1, edgeList[0]);
- } else {
- LineLong = new Line2d(point3, point2, edgeList[2]);
- LineA = new Line2d(point3, point1, edgeList[1]);
- LineB = new Line2d(point1, point2, edgeList[0]);
- }
- }
- // assign points to lines
- is_initialised = true;
- }
- private void drawPix(BufferedImage img, BufferedImage zBuf, double FPDis, int scrX, int scrY, char currentLine, int x, int y, int y1, int y2){
- // 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));
-
- // 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));
- // if the new Z value is greater than the existing Z value on the buffer, the new pixel is calculated and drawn
- if(zBuf.getRGB(x, y) == Color.HSBtoRGB(0, 0, 0) ||
- newZ > zBuf.getRGB(x, y)){ //newZ > zBuf.getRGB(x, y) ||
- zBuf.setRGB(x, y, newZ);
- result = new Point3D(0,0,0);
- //perspectiveMappingMatrix.multiplyPoint3to(new Point3D(x, y, 0), result);
- // project result
- result.projectOn(FPDis, scrX, scrY, result2);
- int colour = texture.getRGB(
- Math.floorMod((int)(result2.x), texture.getWidth()),
- Math.floorMod((int)(result2.y), texture.getHeight()));
- img.setRGB(x, y, (int) (colour - 10*(result2.x/ texture.getWidth()) - 10*(result2.y/ texture.getHeight())));
- }
- }
-}
diff --git a/src/main/java/Vector2D.java b/src/main/java/Vector2D.java
deleted file mode 100644
index 96618ff..0000000
--- a/src/main/java/Vector2D.java
+++ /dev/null
@@ -1,26 +0,0 @@
-public class Vector2D {
- // technically not required as vectors have the same information as points, but it's useful to have separate vector and point things..
- public double x;
- public double y;
-
- public Vector2D(double _x, double _y){
- x = _x;
- y = _y;
- }
- public void createFrom2Points(Point2D start, Point2D end){
- x = end.x - start.x;
- y = end.y - start.y;
- }
- public double getLength(){
- return Math.sqrt(x*x + y*y);
- }
- public double angleTo(Vector2D vec2){
- return Math.acos(
- dot(vec2)
- / (getLength()*vec2.getLength()))
- * Math.signum(x*vec2.y - y *vec2.x);
- }
- public double dot(Vector2D vec2){
- return (x*vec2.x) + (y*vec2.y);
- }
-}
diff --git a/src/main/java/Vector3D.java b/src/main/java/Vector3D.java
deleted file mode 100644
index 6fc0290..0000000
--- a/src/main/java/Vector3D.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// technically not required as vectors have the same information as points, but it's useful to have separate vector and point things..
-
-import java.util.Vector;
-
-public class Vector3D {
- public double x;
- public double y;
- public double z;
-
- public Vector3D(double _x, double _y, double _z){
- x = _x;
- y = _y;
- z = _z;
- }
- public void createFrom2Points(Point3D start, Point3D end){
- x = end.x - start.x;
- y = end.y - start.y;
- z = end.z - start.z;
- }
- public double getLength(){
- return Math.sqrt(x*x + y*y + z*z);
- }
- public double angleTo(Vector3D vec2){
- return Math.acos(
- (x*vec2.x + y*vec2.y + z*vec2.z)
- /( //-----------------------------------------------------------
- Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)) *
- Math.sqrt(Math.pow(vec2.x, 2) + Math.pow(vec2.y, 2) + Math.pow(vec2.z, 2))));
- }
- public Vector3D cross(Vector3D vec2){
- return new Vector3D(
- y*vec2.z - z*vec2.y,
- z*vec2.x - x*vec2.z,
- x*vec2.y - y*vec2.x);
- }
- public double dot(Vector3D vec2){
- return x*vec2.x + y* vec2.y+z* vec2.z;
- }
-}
diff --git a/src/main/java/uk/org/floop/epq3d/App.java b/src/main/java/uk/org/floop/epq3d/App.java
new file mode 100644
index 0000000..99173c4
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/App.java
@@ -0,0 +1,115 @@
+package uk.org.floop.epq3d;
+
+import javax.imageio.ImageIO;
+import javax.swing.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+class App {
+ static ObjectCollection mainCollection;
+ private static void initWindow() {
+ // create a window frame and set the title in the toolbar
+ JFrame window = new JFrame("Testing");
+ // when we close the window, stop the app
+ window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ // create the jpanel to draw on.
+ // this also initializes the game loop
+ initObjects();
+ Screen screen = new Screen(mainCollection);
+ // add the jpanel to the window
+ window.add(screen);
+ window.addKeyListener(screen);
+ window.addMouseListener(screen);
+ window.addMouseMotionListener(screen);
+ // don't allow the user to resize the window
+ window.setResizable(false);
+ // fit the window size around the components (just our jpanel).
+ // pack() should be called after setResizable() to avoid issues on some platforms
+ window.pack();
+ // open window in the center of the screen
+ window.setLocationRelativeTo(null);
+ // display the window
+ window.setVisible(true);
+ }
+
+ public static void main(String[] args) {
+ // invokeLater() is used here to prevent our graphics processing from
+ // blocking the GUI. https://stackoverflow.com/a/22534931/4655368
+ // this is a lot of boilerplate code that you shouldn't be too concerned about.
+ // just know that when main runs it will call initWindow() once.
+ SwingUtilities.invokeLater(App::initWindow);
+ }
+
+ public static void initObjects(){
+ mainCollection = new ObjectCollection();
+
+ BufferedImage testTexture;
+ try {
+ //testTexture = ImageIO.read(new File("/home/cory/Screenshot from 2022-06-06 18-52-12.png"));
+ testTexture = ImageIO.read(new File("/home/cory/Screenshot from 2022-09-26 13-05-40.png"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ mainCollection.addObject(new Object3d(new PointComp[]{
+ new PointComp(-30,-10,-10),
+ new PointComp(-30,-10,10),
+ new PointComp(-30,10,-10),
+ new PointComp(-30,10,10),
+ new PointComp(-10,-10,-10),
+ new PointComp(-10,-10,10),
+ new PointComp(-10,10,-10),
+ new PointComp(-10,10,10)
+
+ }, new int[][]{
+ {0,2,3,1},
+ {4,5,7,6},
+ {0,1,5,4},
+ {1,3,7,5},
+ {3,2,6,7},
+ {2,0,4,6},
+ }, new Point2D[][]{
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ {new Point2D(0, 0), new Point2D(100, 0), new Point2D(100, 100), new Point2D(0, 100)},
+ },
+ true,
+ testTexture,
+ true));
+// mainCollection.addObject(new Object3d(new PointComp[]{
+// new PointComp(-10,10,10),
+// new PointComp(-10,-10,10),
+// new PointComp(10,10,10),
+// new PointComp(10,-10,10),
+// new PointComp(0,0,24)
+// }, new int[][]{
+// {0,2,3,1},
+// {1,0,4},
+// {0,2,4},
+// {2,3,4},
+// {3,1,4}
+// }, true));
+ mainCollection.addObject(new Object3d(new PointComp[]{
+ new PointComp(0,0,0),
+ new PointComp(20,0,0),
+ new PointComp(0,10,0),
+ new PointComp(0,0,10),
+ }, new int[][]{
+ {0,1,2},
+ {0,2,3},
+ {0,3,1},
+ {2,1,3}
+ }, new Point2D[][]{
+ {new Point2D(0,0), new Point2D(100,0), new Point2D(0,100)},
+ {new Point2D(0,0), new Point2D(100,0), new Point2D(0,100)},
+ {new Point2D(0,0), new Point2D(100,0), new Point2D(0,100)},
+ },
+ true,
+ testTexture,
+ true));
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Face.java b/src/main/java/uk/org/floop/epq3d/Face.java
new file mode 100644
index 0000000..426c6cd
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Face.java
@@ -0,0 +1,275 @@
+package uk.org.floop.epq3d;
+
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+public class Face {
+ public PointComp[] points;
+ public Point2D[] UVPoints;
+ public Vector3D normal;
+ public Triangle[] tris;
+ public boolean hasEdges;
+ public BufferedImage texture;
+ public Matrix[] perspectiveMappingMatrices = new Matrix[]{null, null, null, null, null, null};
+ public boolean isInitialised;
+ // fixed face
+ public Face fixedFace;
+ // working variables
+ private final Vector3D traVec = new Vector3D(0,0, 0);
+ // private final Vector2D scaVec = new Vector2D(0,0);
+
+ private final Vector3D real01Vec = new Vector3D(0,0,0);
+ private final Vector2D UV01Vec = new Vector2D(0,0);
+ private final Vector3D real02Vec = new Vector3D(0,0,0);
+ private final Vector2D UV02Vec = new Vector2D(0,0);
+ double[] result;
+ public void initialise(){
+ calculateNormal();
+ separateTris();
+ // fixedFace is a constant object which is used temporarily when the face intersects with the camera,
+ // and thus needs to be sliced.
+ fixedFace = new Face();
+ fixedFace.hasEdges = hasEdges;
+ fixedFace.texture = texture;
+ fixedFace.normal = normal;
+ fixedFace.isInitialised = true;
+ // the fixed face inherits the perspective mapping matrices from the true face. This is because the transforms
+ // stay the same, just the edge bounds of the face have changed.
+ fixedFace.perspectiveMappingMatrices = perspectiveMappingMatrices;
+ isInitialised = true;
+ }
+
+ public void invalidate(){
+ for (Triangle tri:
+ tris) {
+ tri.invalidate();
+ }
+ }
+ public int draw(BufferedImage img, BufferedImage zBuf, BufferedImage debugImg, Matrix camMatrix, double FPDis, int scrX, int scrY){
+ if(!isInitialised){
+ throw new RuntimeException("Face not initialised");
+ }
+ // check for backface culling has been done previously.
+ // initialise points
+ int numberOfPixels = 0;
+ boolean valid = applyPointTransforms(camMatrix, FPDis, scrX, scrY);
+ //bakePerspectiveMatrices();
+
+ if (valid) {
+ 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];
+ boolean lastValid = lastPoint.getRotatedPoint().z > 0.1;
+ boolean thisValid;
+ for (PointComp point:
+ points) {
+ thisValid = point.getRotatedPoint().z > 0.1;
+ // We need to do different things depending on whether the previous point was also a valid point, or not.
+ // first - if only 1 of the last point or this point were valid,
+ // interpolate between them to get the point at the screen. (XOR)
+ if(lastValid ^ thisValid){
+ // solving for z = 0.1 for the line between thisPoint and lastPoint,
+ // separately in the xz and yz planes.
+ double gradX = (point.getRotatedPoint().z - lastPoint.getRotatedPoint().z) /
+ (point.getRotatedPoint().x - lastPoint.getRotatedPoint().x);
+ double gradY = (point.getRotatedPoint().z - lastPoint.getRotatedPoint().z) /
+ (point.getRotatedPoint().y - lastPoint.getRotatedPoint().y);
+
+ newPoints.add(new PointComp(
+ (0.1+gradX*point.getRotatedPoint().x-point.getRotatedPoint().z)/gradX,
+ (0.1+gradY*point.getRotatedPoint().y-point.getRotatedPoint().z)/gradY,
+ 0.1));
+ if(!Double.isFinite(gradX)){
+ newPoints.get(newPoints.size() - 1).point.x = point.getRotatedPoint().x;}
+ if(!Double.isFinite(gradY)){
+ newPoints.get(newPoints.size() - 1).point.y = point.getRotatedPoint().y;}
+ }
+ // finally - if the current point is valid, then add it to the list
+ if(thisValid){
+ newPoints.add(new PointComp(
+ point.getRotatedPoint().x,
+ point.getRotatedPoint().y,
+ point.getRotatedPoint().z));
+ }
+ lastPoint = point;
+ lastValid = thisValid;
+ }
+ // there must be at least 3 points in the face for it to be drawn successfully
+ if(newPoints.size() >= 3) {
+ // finished fixing points, now we need to create a new face consisting of those points.
+ fixedFace.points = newPoints.toArray(new PointComp[0]);
+ fixedFace.separateTris();
+ // invalidate all the points so they are actually calculated
+ for (PointComp point :
+ newPoints) {
+ point.invalidate();
+ }
+ Matrix identMat = new Matrix(3,3);
+ // we use an identity matrix because the points of the fixed face are in camera coordinates already.
+ // we just need the projected 2d point.
+ identMat.setItems(new double[][]{
+ {1,0,0},
+ {0,1,0},
+ {0,0,1}
+ });
+ fixedFace.applyPointTransforms(identMat, FPDis, scrX, scrY);
+ fixedFace.drawTris(img, zBuf, FPDis, scrX, scrY);
+ }
+ }
+ 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:
+ points) {
+ 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);}
+ }
+ return valid;
+ }
+ public void separateTris(){
+ Triangle[] newTris = new Triangle[points.length - 2];
+ for(int i = 0; i< newTris.length; i+=1){
+ newTris[i] = new Triangle(
+ points[0].getProjectedPoint(),
+ points[i+1].getProjectedPoint(),
+ points[i+2].getProjectedPoint(),
+ new boolean[]{hasEdges, hasEdges, hasEdges},
+ texture, perspectiveMappingMatrices[i]);
+ }
+ tris = newTris;
+ }
+ public void calculateNormal(){
+ // too many new variables
+ Point3D point0 = points[0].point;
+ Point3D point1 = points[1].point;
+ Point3D point2;
+ Vector3D vec1 = new Vector3D(point1.x - point0.x, point1.y - point0.y, point1.z - point0.z);
+ Vector3D vec2 = new Vector3D(0,0,0); // initialisation otherwise intellij gets mad
+ // find a vector which is not inline with other vectors
+ boolean valid = false; int i = 2;
+ while(!valid && i < points.length) {
+ point2 = points[i].point;
+ vec2 = new Vector3D(point2.x - point0.x, point2.y - point0.y, point2.z - point0.z);
+ double angle = Math.abs(vec1.angleTo(vec2));
+ if(angle > 0.1 && angle < 2*Math.PI - 0.1){
+ // if the angle between the vectors is between a threshold, the two vectors are valid.
+ // else, calculate the second vector using a different set of points.
+ valid = true;
+ }}
+ if(!valid){throw new RuntimeException("Could not calculate normal of face");}
+ normal = vec1.cross(vec2);
+ }
+// private void bakePerspectiveMatrices() {
+// // one mapping matrix for each triangle
+// // to achieve perspective mapping, we need to convert from the 2d screen position, to the 3d world position by
+// // reverse projecting and interpolating in the triangle - this gives camera coordinates.
+// // next, we need to use the inverse camera matrix to convert from camera coordinates to world coordinates.
+// // then, we can use the perspective mapping matrix unique and baked to each triangle on each face to convert from those world coordinates, into uv coordinates.
+//
+//
+//
+// real01Vec.createFrom2Points(points[0].getRotatedPoint(), points[1].getRotatedPoint());
+// real02Vec.createFrom2Points(points[0].getRotatedPoint(), points[2].getRotatedPoint());
+// // scaVec.createFrom2Points(points[0].getProjectedPoint(), points[1].getProjectedPoint());
+// UV01Vec.createFrom2Points(UVPoints[0], UVPoints[1]);
+// UV02Vec.createFrom2Points(UVPoints[0], UVPoints[2]);
+// // it must remain as the same object so pointers elsewhere still work.
+// // invert x and y coordinates because in rotated coordinates, they are the wrong way round.
+// perspectiveMappingMatrix.setItems(new double[][]{
+// {0,1,0,0},
+// {1,0,0,0},
+// {0,0,1,0},
+// {0,0,0,1},
+// });
+//
+// traVec.createFrom2Points(new Point3D(UVPoints[0].y, UVPoints[0].x, 0), points[0].getRotatedPoint());
+// Matrix tMat = new Matrix(4, 4);
+//// tMat.setItems(new double[][]{
+//// {1, 0, 0, traVec.x},
+//// {0, 1, 0, traVec.y},
+//// {0, 0, 1, traVec.z},
+//// {0, 0, 0, 1},
+//// });
+// tMat.setItems(new double[][]{
+// {1, 0, 0, 0},
+// {0, 1, 0, 0},
+// {0, 0, 1, points[0].getRotatedPoint().z},
+// {0, 0, 0, 1},
+// });
+// double scale = 0.1;//(real01Vec.getLength() / UV01Vec.getLength());
+// Matrix scaMat = new Matrix(4, 4);
+// scaMat.setItems(new double[][]{
+// {scale, 0, 0, 0},
+// {0, scale, 0, 0},
+// {0, 0, scale, 0},
+// {0, 0, 0, 1}
+// });
+// // find z rotation and define matrix
+// double zAng = new Vector2D(real01Vec.x, real01Vec.y).angleTo(UV01Vec);
+// Matrix zMat = new Matrix(4, 4);
+// zMat.setItems(new double[][]{
+// {Math.cos(zAng), Math.sin(zAng), 0, 0},
+// {-Math.sin(zAng), Math.cos(zAng), 0, 0},
+// {0, 0, 1, 0},
+// {0, 0, 0, 1}}
+// );
+// // rotate "real" vectors using the Z matrix
+// result = zMat.multiplyPoint3raw(real01Vec.x, real01Vec.y, real01Vec.z);
+// real01Vec.x = result[0];real01Vec.y = result[1];real01Vec.z = result[2];
+// result = zMat.multiplyPoint3raw(real02Vec.x, real02Vec.y, real02Vec.z);
+// real02Vec.x = result[0];real02Vec.y = result[1];real02Vec.z = result[2];
+// // invert the Z matrix (todo cleanup)
+// zMat.setItems(new double[][]{
+// {Math.cos(-zAng), Math.sin(-zAng), 0, 0},
+// {-Math.sin(-zAng), Math.cos(-zAng), 0, 0},
+// {0, 0, 1, 0},
+// {0, 0, 0, 1}}
+// );
+// // find Y rotation and define matrix
+// double yAng = new Vector2D(real01Vec.x, real01Vec.z).angleTo(new Vector2D(UV01Vec.x, 0));
+// Matrix yMat = new Matrix(4, 4);
+// yMat.setItems(new double[][]{
+// {Math.cos(yAng), 0, -Math.sin(yAng), 0},
+// {0, 1, 0, 0},
+// {Math.sin(yAng), 0, Math.cos(yAng), 0},
+// {0, 0, 0, 1}}
+// );
+// result = yMat.multiplyPoint3raw(real02Vec.x, real02Vec.y, real02Vec.z);
+// real02Vec.x = result[0];real02Vec.y = result[1];real02Vec.z = result[2];
+// yMat.setItems(new double[][]{
+// {Math.cos(-yAng), 0, -Math.sin(-yAng), 0},
+// {0, 1, 0, 0},
+// {Math.sin(-yAng), 0, Math.cos(-yAng), 0},
+// {0, 0, 0, 1}}
+// );
+// double xAng = new Vector2D(real02Vec.y, real02Vec.z).angleTo(new Vector2D(UV01Vec.x, 0)); // this is fine
+// Matrix xMat = new Matrix(4, 4);
+// xMat.setItems(new double[][]{
+// {1, 0, 0, 0},
+// {0, Math.cos(-xAng), Math.sin(-xAng), 0},
+// {0, -Math.sin(-xAng), Math.cos(-xAng), 0},
+// {0, 0, 0, 1}}
+// );
+// // Matrix that returns the position on a texture from 3d camera space on the face of an object
+// // we already know the Z depth because of calculations for Z buffers.
+// //perspectiveMappingMatrix.multiply(scaMat);
+// perspectiveMappingMatrix.multiply(scaMat.multiplyGetResult(tMat));
+// //perspectiveMappingMatrix.multiply((scaMat.multiplyGetResult(xMat.multiplyGetResult(yMat.multiplyGetResult(zMat.multiplyGetResult(tMat))))));
+// // perspectiveMappingMatrix.multiply(scaMat.multiplyGetResult(zMat.multiplyGetResult(yMat.multiplyGetResult(xMat.multiplyGetResult(tMat)))));
+// }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/HTTPPost.java b/src/main/java/uk/org/floop/epq3d/HTTPPost.java
new file mode 100644
index 0000000..c46cb8b
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/HTTPPost.java
@@ -0,0 +1,46 @@
+package uk.org.floop.epq3d;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+public class HTTPPost {
+ public void invokePost(JsonWriter json) {
+
+ try {
+ String requestBody = json.file.toJSONString();
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest
+ .newBuilder()
+ .uri(URI.create("http://localhost:8080"))
+ .POST(HttpRequest.BodyPublishers.ofString(requestBody))
+ .header("Accept", "application/json")
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ //System.out.println(response.body());
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+// private String prepareRequest() throws JsonProcessingException {
+// var values = new HashMap() {
+// {
+// put("Id", "12345");
+// put("Customer", "Roger Moose");
+// put("Quantity", "3");
+// put("Price","167.35");
+// }
+// };
+//
+// var objectMapper = new ObjectMapper();
+// String requestBody = objectMapper.writeValueAsString(values);
+// return requestBody;
+// }
+
+}
+
diff --git a/src/main/java/uk/org/floop/epq3d/Line2d.java b/src/main/java/uk/org/floop/epq3d/Line2d.java
new file mode 100644
index 0000000..5054308
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Line2d.java
@@ -0,0 +1,140 @@
+package uk.org.floop.epq3d;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+// handles line and line drawing
+public class Line2d {
+ public Point2D point1;
+ public Point2D point2;
+
+ public boolean isDrawn;
+
+ // initializer variables
+ boolean is_initialised = false;
+ private Point2D realPoint1; private Point2D realPoint2;
+ private int dx;
+ private int dy; private int sDy;
+ private char iterator;
+ private Point2D xy;
+ int[] returnVal = new int[]{0,0};
+ private double gradient; // z gradient of the line in terms of X
+
+ // drawing progress variables
+ private int iteratorVal;
+ private int D;
+ public Line2d(Point2D _point1, Point2D _point2, boolean _isDrawn){
+ point1 = _point1;
+ point2 = _point2;
+ isDrawn = _isDrawn;
+ }
+
+ /**
+ * Draws onto a given image
+ * @param img the image to draw onto
+ */
+ public void draw(BufferedImage img){
+ if (!is_initialised){initialise();}
+ if (iterator == 'x'){
+ for (int i = 0; i <= dx; i++){
+ nextPix();
+ 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++){
+ nextPix();
+ 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(.5f, 1, 1));
+ }
+ }
+ }
+ is_initialised = false;
+ /*
+ debug - draws the start and end of each line in white and green
+ img.setRGB(realPoint1.intX(), realPoint1.intY(), Color.blue.getRGB());
+ img.setRGB(realPoint2.intX(), realPoint2.intY(), Color.green.getRGB());
+ */
+ }
+
+ /**
+ * Performs the initial calculations required to draw the line
+ * Is automatically called whenever nextPix() or draw() are called when initialise() has not run.
+ */
+ public void initialise(){
+ // initialise brensenham algorithm
+ if (point2.x > point1.x) {
+ realPoint1 = point1;
+ realPoint2 = point2;
+ } else { // if dx is less than zero, swap the points around
+ realPoint1 = point2;
+ realPoint2 = point1;
+ }
+ dx = realPoint2.x - realPoint1.x;
+ dy = realPoint2.y - realPoint1.y;
+ sDy = (int) Math.signum(dy); dy = Math.abs(dy);
+
+ xy = new Point2D(realPoint1.x,realPoint1.y); // starting point
+
+ // check if dy is greater than or less than dx
+ if (dy < dx){
+ iterator = 'x';
+ D = (2*dy) - dx;
+ }else{
+ iterator='y';
+ D = (2*dx) - dy;
+ }
+ iteratorVal = 0;
+
+ // init other variables
+ // dz / dx
+ gradient = (realPoint2.z-realPoint1.z) /
+ (realPoint2.x - realPoint1.x);
+ is_initialised = true;
+ }
+
+ /**
+ * @return the x and y coordinate of the next pixel in the line.
+ */
+ public int[] nextPix(){
+ if(!is_initialised){initialise();}
+ returnVal[0] = xy.x;
+ returnVal[1] = xy.y;
+ if (iterator=='x' && iteratorVal < dx && iteratorVal != -1){
+ if (D > 0) {
+ D += 2 * (dy - dx);
+ xy.y += sDy;
+ } else {
+ D += 2*dy;
+ }
+ xy.x += 1; // the line is always drawn left to right
+ } else if(iterator =='y' && iteratorVal < dy && iteratorVal != -1) {
+ if (D > 0) {
+ D += 2 * (dx-dy);
+ xy.x += 1; // the line is always drawn left to right
+ } else {
+ D += 2*dx;
+ }
+ xy.y += sDy;
+
+ }
+ else if(iteratorVal != -1) {
+ iteratorVal = -1;
+ returnVal[0] = realPoint2.x;
+ returnVal[1] = realPoint2.y;
+ return returnVal;
+ }
+ else {
+ is_initialised = false;
+ throw new RuntimeException("Accessed too many line pixels");
+ }
+ iteratorVal += 1;
+ return returnVal;
+ }
+ public double getZVal(int x){
+ return realPoint1.z + (gradient * (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
new file mode 100644
index 0000000..5c980a9
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Matrix.java
@@ -0,0 +1,211 @@
+package uk.org.floop.epq3d;
+
+public class Matrix {
+ protected int x;
+ protected int y;
+ private double[][] items;
+
+ public Matrix(int _x, int _y){
+ x = _x; y= _y;
+ items = new double[y][x];
+ }
+ public double getItem(int _x, int _y){
+ return items[_y][_x];
+ }
+ public void setItem(int _x, int _y, double val){
+ items[_y][_x] = val;
+ }
+ public double[][] getItems() {
+ return items;
+ }
+ public void setItems(double[][] newItems){
+ items = newItems;
+ }
+ public Matrix multiplyGetResult(Matrix multiplier) {
+ Matrix result = new Matrix(multiplier.x, this.y);
+ 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= distance){
+ pointA = points[i].point;
+ distance = newDis;}}
+ for (PointComp point : points) {
+ newDis = Math.pow(pointA.x - point.point.x, 2) +
+ Math.pow(pointA.y - point.point.y, 2) +
+ Math.pow(pointA.z - point.point.z, 2);
+ if (newDis >= distance){
+ pointB = point.point;
+ distance = newDis;}}
+ boundingSphereC = new Point3D(
+ (pointA.x + pointB.x) / 2,
+ (pointA.y + pointB.y)/2,
+ (pointA.z + pointB.z)/2);
+ boundingSphereR = Math.sqrt(distance)/2;
+ for (PointComp point:
+ points) {
+ distance = Math.sqrt(
+ Math.pow(boundingSphereC.x - point.point.x, 2)+
+ Math.pow(boundingSphereC.y - point.point.y, 2)+
+ Math.pow(boundingSphereC.z - point.point.z, 2));
+ if(distance > boundingSphereR){
+ boundingSphereR = distance;
+ }
+ }
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/ObjectCollection.java b/src/main/java/uk/org/floop/epq3d/ObjectCollection.java
new file mode 100644
index 0000000..5e2cd6b
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/ObjectCollection.java
@@ -0,0 +1,129 @@
+package uk.org.floop.epq3d;
+
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+// stores both objects and other object collections
+public class ObjectCollection {
+ public ArrayList points = new ArrayList();
+ private Object3d collectionObject;
+ public double boundingSphereR;
+ public Point3D boundingSphereC;
+
+
+ public ArrayList subCollections = new ArrayList();
+ public ArrayList objects = new ArrayList();
+
+ public void invalidate(boolean invalidatePoints){
+ // the first level of object collections should contain all the points for the sublevels.
+ // this means that we only need to invalidate them at the top level
+ if(invalidatePoints){for (PointComp point:
+ points) {
+ point.invalidate();
+ }}
+ for(ObjectCollection subCollection:
+ subCollections){
+ subCollection.invalidate(false);
+ }
+ for(Object3d object:
+ objects){
+ object.invalidate();
+ }
+ }
+
+ public void draw(BufferedImage img, BufferedImage zBuf, BufferedImage debugImg, Matrix camMatrix, Point3D playerPos, Plane[] frustumPlanes, double FPDis, int scrX, int scrY){
+ for (Object3d object:
+ objects) {
+ boolean draw = true;
+ int i = 0;
+ for (Plane plane:
+ frustumPlanes) {
+ 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);}
+ }
+ // todo check for frustum culling
+ for(ObjectCollection collection:
+ subCollections){
+ boolean draw = true;
+ int i = 0;
+ for (Plane plane:
+ frustumPlanes) {
+ 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);
+ }
+ }
+ public void addCollection(int[] pointList){
+ subCollections.add(new ObjectCollection());
+ }
+ // making sure that all the point indices make sense might be a nightmare but ehh
+ public void addObject(Object3d object){
+ PointComp[] newpoints = object.points;
+ objects.add(object);
+ // add the new object
+
+ // merge lists
+ for (PointComp newpoint : newpoints) {
+ boolean found = false;
+ // find out if any of the new points already exist in the list
+ for (PointComp point : points) {
+ if (newpoint == point) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ points.add(newpoint);
+ }
+ }
+ }
+ public void initialise(){
+ // init bounding sphere
+ double distance = 0;
+ double newDis;
+ Point3D pointA = points.get(0).point;
+ Point3D pointB = points.get(0).point;
+ // todo - maybe use some vector classes?
+ for (int i = 1; i < points.size(); i+=1) {
+ newDis = Math.pow(points.get(0).point.x - points.get(i).point.x, 2) +
+ Math.pow(points.get(0).point.y - points.get(i).point.y, 2) +
+ Math.pow(points.get(0).point.z - points.get(i).point.z, 2);
+ if (newDis >= distance){
+ pointA = points.get(i).point;
+ distance = newDis;}}
+ for (PointComp point : points) {
+ newDis = Math.pow(pointA.x - point.point.x, 2) +
+ Math.pow(pointA.y - point.point.y, 2) +
+ Math.pow(pointA.z - point.point.z, 2);
+ if (newDis >= distance){
+ pointB = point.point;
+ distance = newDis;}}
+ boundingSphereC = new Point3D(
+ (pointA.x + pointB.x) / 2,
+ (pointA.y + pointB.y)/2,
+ (pointA.z + pointB.z)/2);
+ boundingSphereR = Math.sqrt(distance)/2;
+ boolean valid = false;
+ for (PointComp point:
+ points) {
+ distance = Math.sqrt(
+ Math.pow(boundingSphereC.x - point.point.x, 2)+
+ Math.pow(boundingSphereC.y - point.point.y, 2)+
+ Math.pow(boundingSphereC.z - point.point.z, 2));
+ if(distance > boundingSphereR){
+ boundingSphereR = distance;
+ }
+ }
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Plane.java b/src/main/java/uk/org/floop/epq3d/Plane.java
new file mode 100644
index 0000000..586a272
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Plane.java
@@ -0,0 +1,29 @@
+package uk.org.floop.epq3d;
+
+public class Plane {
+ public Point3D mainPoint;
+ public Vector3D normalVector;
+
+ public double getDistance(Point3D point){
+ Vector3D vec = new Vector3D(0,0,0);
+ vec.createFrom2Points(mainPoint, point);
+ return normalVector.dot(vec);
+ }
+ public void initFrom3Points(Point3D _mainPoint, Point3D point1, Point3D point2){
+ // find normal vector to 3 points
+ Vector3D vec1 = new Vector3D(0,0,0);
+ Vector3D vec2 = new Vector3D(0,0,0);
+ vec1.createFrom2Points(_mainPoint, point1);
+ vec2.createFrom2Points(point1, point2);
+ initFromPointAndVector(_mainPoint, vec1.cross(vec2));
+ }
+ public void initFromPointAndVector(Point3D _mainPoint, Vector3D _normalVector){
+ // convert to unit vector
+ mainPoint = _mainPoint;
+ double len = _normalVector.getLength();
+ normalVector = new Vector3D(
+ _normalVector.x / len,
+ _normalVector.y / len,
+ _normalVector.z / len);
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Player.java b/src/main/java/uk/org/floop/epq3d/Player.java
new file mode 100644
index 0000000..a02f22a
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Player.java
@@ -0,0 +1,225 @@
+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
+ private BufferedImage image;
+ // 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 Point3D position = new Point3D(0,0,0);
+ private Point3D rotation = new Point3D(0,Math.PI / 2, 0);
+ private Point3D direction = new Point3D(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 {
+ Point3D dirvec = new Point3D(0,0,0);
+ izMat.multiplyPoint3to(direction, dirvec);
+ 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);
+ // todo - work out why i need to reverse this
+ 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].initFromPointAndVector(position, viewVector);
+ 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;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/uk/org/floop/epq3d/Point2D.java b/src/main/java/uk/org/floop/epq3d/Point2D.java
new file mode 100644
index 0000000..a18ca1d
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Point2D.java
@@ -0,0 +1,22 @@
+package uk.org.floop.epq3d;
+
+import java.util.ArrayList;
+
+public class Point2D {
+ public int x;
+ public int y;
+ public double z;
+ // contains a Z value so that projected points can have their Z values calculated efficiently
+ public Point2D(int _x, int _y){
+ x = _x;
+ y = _y;
+ }
+ public void set(int _x, int _y){
+ x = _x;
+ y = _y;
+ }
+ public int[] get(){
+ return new int[]{x, y};
+ }
+
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Point3D.java b/src/main/java/uk/org/floop/epq3d/Point3D.java
new file mode 100644
index 0000000..3eda4ed
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Point3D.java
@@ -0,0 +1,38 @@
+package uk.org.floop.epq3d;
+
+public class Point3D {
+ public double x;
+ public double y;
+ public double z;
+
+ public Point3D(double _x, double _y, double _z){
+ x = _x;
+ y = _y;
+ z = _z;
+ }
+ public void set(double[] _new){
+ x = _new[0];
+ y = _new[1];
+ z = _new[2];
+ }
+ public void translate(Point3D trVec){
+ x += trVec.x;
+ y += trVec.y;
+ z += trVec.z;
+ }
+ public Point2D project(double fpdis, int scrX, int scrY){
+ return new Point2D(
+ scrX - (int)(scrX*0.5*((fpdis*y)/(z) + 1)),
+ (int)(scrX*0.5*((fpdis*x)/(z) + ((double)scrY/(double)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
+ point.x = scrX - (int)(scrX*0.5*((fpdis*y)/(z) + 1));
+ point.y = (int)(scrX*0.5*((fpdis*x)/(z) + ((double)scrY/(double)scrX)));
+ }
+ public double[] get(){
+ return new double[]{x, y, z};
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/PointComp.java b/src/main/java/uk/org/floop/epq3d/PointComp.java
new file mode 100644
index 0000000..934be18
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/PointComp.java
@@ -0,0 +1,42 @@
+package uk.org.floop.epq3d;
+
+public class PointComp {
+ public Point3D point = new Point3D(0,0,0);
+ private final Point3D rotatedPoint = new Point3D(0,0,0);
+ private boolean isRotatedPointValid = true;
+ private final Point2D projectedPoint = new Point2D(0,0);
+ private boolean isProjectedPointValid = true;
+ public PointComp(double _x, double _y, double _z){
+ point.x = _x;
+ point.y = _y;
+ point.z = _z;
+ }
+ public void invalidate(){
+ isProjectedPointValid = false; isRotatedPointValid = false;
+ }
+ public Point3D getRotatedPoint(){
+ if(!isRotatedPointValid){
+ throw new RuntimeException("Rotated point not initialised");
+ }
+ return rotatedPoint;
+ }
+ public Point2D getProjectedPoint(){
+ if(!isRotatedPointValid){
+ throw new RuntimeException("Projected point not initialised");
+ }
+ return projectedPoint;
+ }
+ public void setRotatedPoint(Matrix camMatrix){
+ if(!isRotatedPointValid) {
+ camMatrix.multiplyPoint3to(point, rotatedPoint);
+ isRotatedPointValid = true;
+ }
+ }
+ public void setProjectedPoint(double FPDis, int scrX, int scrY){
+ if(!isProjectedPointValid) {
+ rotatedPoint.projectOn(FPDis, scrX, scrY, projectedPoint);
+ projectedPoint.z = rotatedPoint.z;
+ isProjectedPointValid = true;
+ }
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Screen.java b/src/main/java/uk/org/floop/epq3d/Screen.java
new file mode 100644
index 0000000..b9ed661
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Screen.java
@@ -0,0 +1,290 @@
+package uk.org.floop.epq3d;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+
+public class Screen extends JPanel implements ActionListener, KeyListener, MouseListener, MouseMotionListener {
+ // __working variables__
+ public long lastTime = 0;
+ // mouse
+ private boolean captured = false;
+ private final Point2D mouseRel = new Point2D(0,0);
+ private final Cursor invisibleCursor;
+
+ public ObjectCollection mainCollection;
+ public JsonWriter json;
+
+ // testing\
+// private Line2d line = new Line2d(
+// new Point2D(200, 200),
+// new Point2D(1, 1),
+// true);
+// private Triangle Triangle = new Triangle(
+// new Point2D(200, 200), new Point2D(1, 1), new Point2D(400,200)
+// ,new boolean[]{false, false, false},
+// null,
+// new Matrix(3, 3));
+// private final Point3D[] points = new Point3D[]{
+// new Point3D(10,10,-1),
+// new Point3D(-10, 10, -1),
+// new Point3D(-10, -10, -1),
+// new Point3D(10, -10, -1)};
+ // double ang = 0;
+ // __config variables__
+ // controls the delay between each tick in ms
+ private final int DELAY = 25;
+ // controls the size of the board
+ public static final int TILE_SIZE = 50;
+ public static final int ROWS = 12;
+ public static final int COLUMNS = 18;
+ // suppress serialization warning
+ private static final long serialVersionUID = 490905409104883233L;
+
+ // keep a reference to the frameTimer object that triggers actionPerformed() in
+ // case we need access to it in another method
+ private final Timer frameTimer;
+ private final Player player;
+
+ public Screen(ObjectCollection _mainCollection) {
+ mainCollection = _mainCollection;
+
+ // set the game board size
+ setPreferredSize(new Dimension(TILE_SIZE * COLUMNS, TILE_SIZE * ROWS));
+ // 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)
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Point hotSpot = new Point(0,0);
+ BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT);
+ invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor");
+
+ // initialize the game state
+ player = new Player(TILE_SIZE*COLUMNS, TILE_SIZE*ROWS);
+ // this frameTimer will call the actionPerformed() method every DELAY ms
+ frameTimer = new Timer(DELAY, this);
+ frameTimer.start();
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // called by the frameTimer every DELAY ms.
+ // runs 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.
+ repaint();
+ }
+
+ @Override
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ // when calling g.drawImage() we can use "this" for the ImageObserver
+ // because Component implements the ImageObserver interface, and JPanel
+ // extends from Component. So "this" Board instance, as a Component, can
+ // react to imageUpdate() events triggered by g.drawImage()
+
+ // draw graphics.
+ drawScreen(g);
+ // player.draw(g, this);
+
+ // this smooths out animations on some systems
+ Toolkit.getDefaultToolkit().sync();
+ }
+ private void drawScreen(Graphics g) {
+ BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB );
+ BufferedImage zBuf = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
+ BufferedImage debugImg = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
+ debugImg.createGraphics();
+ g.setColor(Color.white);
+
+// System.out.println(zBuf.getRGB(0,0));
+// zBuf.setRGB(0,0, 100);
+// System.out.println(zBuf.getRGB(0,0));
+
+// line.point2.x = 100*Math.sin(ang) + 200;
+// line.point2.y = 100*Math.cos(ang) + 200;
+// Triangle.point2.x = 100*Math.sin(ang+3) + 200;
+// Triangle.point2.y = 100*Math.cos(ang+3) + 200;
+// line.draw(img);
+ /* ArrayList newPoints = new ArrayList();
+ for (Point3D point: points) {
+ Point3D _new = new Point3D(0,0,0);
+ player.camMatrix.multiplyPoint3to(point, _new);
+ if(_new.z > .1) {
+ newPoints.add(_new);
+ } else{
+ newPoints.add(null);
+ }
+ }
+ Triangle t1 = null;
+ Triangle t2 = null;
+ try {
+ t1 = new Triangle(
+ newPoints.get(0).project(player.FPDis, getWidth(), getHeight()),
+ newPoints.get(1).project(player.FPDis, getWidth(), getHeight()),
+ newPoints.get(2).project(player.FPDis, getWidth(), getHeight()));
+ } catch(NullPointerException ignored){}
+ try{
+ t2 = new Triangle(
+ newPoints.get(0).project(player.FPDis, getWidth(), getHeight()),
+ newPoints.get(2).project(player.FPDis, getWidth(), getHeight()),
+ newPoints.get(3).project(player.FPDis, getWidth(), getHeight()));
+ } catch(NullPointerException ignored){}
+
+ try{t1.draw(img);}catch (NullPointerException ignored){}
+ try{t2.draw(img);}catch (NullPointerException ignored){}
+ ang += 0.02;*/
+
+ mainCollection.invalidate(true);
+ mainCollection.draw(img, zBuf, debugImg, player.camMatrix, player.getPos(), player.frustumPlanes, player.Fpdis, getWidth(), getHeight());
+ g.drawImage(img, 0, 0, this);
+
+ // DEBUG DRAWING
+ {
+ debugImg.getGraphics().drawString(Math.round(1000 / (float) (System.currentTimeMillis() - lastTime)) + " fps", 10, 10);
+ 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: " +
+ String.format("%.2f", player.getPos().x) + " " +
+ String.format("%.2f", player.getPos().y) + " " +
+ String.format("%.2f", player.getPos().z) + " ", 100, 50);
+ 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: " +
+ 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 + ": " +
+ 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);
+ }
+ // write debug overlay
+ g.drawImage(debugImg, 0, 0, this);
+ lastTime = System.currentTimeMillis();
+ }
+ HTTPPost post = new HTTPPost();
+ // json WRITING
+ json = new JsonWriter();
+ Point3D[] vertexList;
+ int[][] faceList;
+
+ // camera object
+ vertexList = new Point3D[5];
+ faceList = new int[][]{{0, 1, 2, 3}};
+ for (int i = 0; i < 4; i += 1) {
+ vertexList[i] = player.ScreenCornerPosS[i].getRotatedPoint();
+ }
+ vertexList[4] = player.FPWorldPos;
+ json.addBlenderObject(vertexList, faceList);
+
+ post.invokePost(json);
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {}
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ int key = e.getKeyCode();
+
+ if (key == KeyEvent.VK_ESCAPE){
+ captured = false;
+ setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+ }
+ // pass key-press to the player, so they can handle it as well
+ player.keyPressed(key);
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ int key = e.getKeyCode();
+
+ // pass key-release to the player, so they can handle it as well
+ player.keyReleased(key);
+ }
+
+ // gets the relative position of the mouse since the last time this function was called
+ public Point2D get_mouse_rel(){
+ Point2D rel;
+ // this doesn't work if the mouse isn't captured. If I need it to, I can
+ // probably make it, but I don't know what the functionality looks like.
+ if (captured) {
+ rel = new Point2D(MouseInfo.getPointerInfo().getLocation().x - getLocationOnScreen().x - getSize().width / 2 + mouseRel.x,
+ MouseInfo.getPointerInfo().getLocation().y - getLocationOnScreen().y - getSize().height / 2 + mouseRel.y);
+ mouseRel.set(0, 0);
+ // set the position of the mouse back to (0,0)
+ try {
+ Robot robot = new Robot();
+ robot.mouseMove(getLocationOnScreen().x + getSize().width / 2,
+ getLocationOnScreen().y + getSize().height / 2);
+ } catch (AWTException ex) {
+ ex.printStackTrace();
+ }
+ }
+ else{
+ rel = new Point2D(0,0);
+ }
+ return rel;
+ }
+ @Override
+ public void mouseClicked(MouseEvent mouseEvent) {
+ if (mouseEvent.getButton() == MouseEvent.BUTTON1){
+ captured = true;
+ get_mouse_rel();
+ setCursor(invisibleCursor);
+ }
+ }
+
+ @Override
+ public void mousePressed(MouseEvent mouseEvent) {}
+
+ @Override
+ public void mouseReleased(MouseEvent mouseEvent) {}
+
+ @Override
+ public void mouseEntered(MouseEvent mouseEvent) {}
+
+ @Override
+ public void mouseExited(MouseEvent mouseEvent) {
+ if (captured) {
+ // add current mouse relative location to mouseRel so mouse movements are not lost
+ mouseRel.x += mouseEvent.getX() - getSize().width / 2d;
+ mouseRel.y += mouseEvent.getY() - getSize().height / 2d;
+
+ // System.out.println(mouseEvent.getX() + " " + mouseEvent.getY());
+ // set the position of the mouse back to (0,0)
+ try {
+ Robot robot = new Robot();
+ robot.mouseMove(getLocationOnScreen().x + getSize().width / 2,
+ getLocationOnScreen().y + getSize().height / 2);
+ } catch (AWTException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent mouseEvent) {}
+
+ @Override
+ public void mouseMoved(MouseEvent mouseEvent) {
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/uk/org/floop/epq3d/Triangle.java b/src/main/java/uk/org/floop/epq3d/Triangle.java
new file mode 100644
index 0000000..89b58b0
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Triangle.java
@@ -0,0 +1,178 @@
+package uk.org.floop.epq3d;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public class Triangle{
+ public Point2D point1;
+ public Point2D point2;
+ public Point2D point3;
+
+ public Matrix perspectiveMappingMatrix;
+
+
+ public boolean[] edgeList; //edge 1-2 , 2-3, 3-1
+
+ private Line2d LineLong;
+ private Line2d LineA;
+ private Line2d LineB;
+
+ public BufferedImage texture;
+
+ // initialisation variables
+ private boolean is_initialised = false;
+ private Point2D min;
+ private Point2D max;
+ // progress variables
+ private Point3D result = new Point3D(0,0,0);
+ private Point2D result2 = new Point2D(0,0);
+ public void invalidate() {
+ is_initialised = false;
+ }
+ public Triangle(Point2D _pA, Point2D _pB, Point2D _pC, boolean[] _edgeList, BufferedImage _texture, Matrix mapMatrix){
+ point1 = _pA;
+ point2 = _pB;
+ point3 = _pC;
+ edgeList = _edgeList;
+ texture = _texture;
+ perspectiveMappingMatrix = mapMatrix;
+ }
+ // returns int for debug
+ public int draw(BufferedImage img, BufferedImage zBuf, double FPDis, int scrX, int scrY){
+ //
+ long lastMillis;
+
+
+ if (!is_initialised){initialise();}
+ int[] point1;
+ int[] point2;
+ char currentLine = 'A';
+
+ lastMillis = System.currentTimeMillis();
+
+ point1 = LineLong.nextPix();
+ point2 = LineA.nextPix();
+ 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] < img.getWidth() && point1[1] < img.getHeight()){
+ img.setRGB(point1[0], point1[1], Color.HSBtoRGB(0, 1, 1));
+ }
+ try { // this error seems to be thrown randomly, for various reasons // todo fix
+ point1 = LineLong.nextPix();
+ }
+ catch (Exception e){
+ throw new RuntimeException("accessed too many line pixels");
+ }
+ }
+ while(x-1 == point2[0]) {
+ if (currentLine == 'A') {
+ try{
+ if(LineA.isDrawn && // draw line pixels if needed, and on screen
+ point2[0] > 0 && point2[1] > 0 && point2[0] < img.getWidth() && point2[1] < img.getHeight()){
+ img.setRGB(point2[0], point2[1], Color.HSBtoRGB(0f, 1, 1));
+ }
+ point2 = LineA.nextPix();
+ }
+ catch (RuntimeException e){
+ currentLine = 'B';
+ // point2 = LineB.nextPix();
+ }
+ }
+ else {
+ if(LineB.isDrawn && // draw line pixels if needed, and on screen
+ point2[0] > 0 && point2[1] > 0 && point2[0] < img.getWidth() && point2[1] < img.getHeight()){
+ img.setRGB(point2[0], point2[1], Color.HSBtoRGB(0f, 1, 1));
+ }
+ point2 = LineB.nextPix();
+ }
+ }
+ // cancel drawing if the x value of the triangle is out of bounds
+ if (x >= img.getWidth()) {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], img.getHeight() - 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]);
+ }
+ } else {
+ for (int y = Math.max(point2[1], 0); y <= Math.min(point1[1], img.getHeight() - 1); y += 1) {
+ drawPix(img, zBuf, FPDis, scrX, scrY, currentLine, x,y, point1[1], point2[1]);
+ }
+ }
+ }
+ }
+ lastMillis = (System.currentTimeMillis() - lastMillis);
+ return (int)lastMillis;
+ }
+ public void initialise(){
+ if (point1 == null || point2 == null || point3 == null){
+ throw new NullPointerException();
+ }
+ min = new Point2D(Math.min(point1.x, Math.min(point2.x, point3.x)), Math.min(point1.y, Math.min(point2.y, point3.y)));
+ max = new Point2D(Math.max(point1.x, Math.max(point2.x, point3.x)), Math.max(point1.y, Math.max(point2.y, point3.y)));
+ // woo horrible IFs mess.
+ // we need to figure out which points touch the edges in order to find which line is the 'full length' edge,
+ // and then assign line A and line B in order
+ if (point1.x == min.x) {
+ if (point2.x == max.x){
+ LineLong = new Line2d(point1, point2, edgeList[0]);
+ LineA = new Line2d(point1, point3, edgeList[2]);
+ LineB = new Line2d(point3, point2, edgeList[1]);
+ } else {
+ LineLong = new Line2d(point1, point3, edgeList[2]);
+ LineA = new Line2d(point1, point2, edgeList[0]);
+ LineB = new Line2d(point2, point3, edgeList[1]);
+ }
+ }
+ else if (point2.x == min.x) {
+ if (point1.x == max.x) {
+ LineLong = new Line2d(point2, point1, edgeList[0]);
+ LineA = new Line2d(point2, point3, edgeList[1]);
+ LineB = new Line2d(point3, point1, edgeList[2]);
+ } else {
+ LineLong = new Line2d(point2, point3, edgeList[1]);
+ LineA = new Line2d(point2, point1, edgeList[0]);
+ LineB = new Line2d(point1, point3, edgeList[2]);
+ }
+ }
+ else if (point3.x == min.x){
+ if (point1.x == max.x) {
+ LineLong = new Line2d(point3, point1, edgeList[2]);
+ LineA = new Line2d(point3, point2, edgeList[1]);
+ LineB = new Line2d(point2, point1, edgeList[0]);
+ } else {
+ LineLong = new Line2d(point3, point2, edgeList[2]);
+ LineA = new Line2d(point3, point1, edgeList[1]);
+ LineB = new Line2d(point1, point2, edgeList[0]);
+ }
+ }
+ // assign points to lines
+ is_initialised = true;
+ }
+ private void drawPix(BufferedImage img, BufferedImage zBuf, double FPDis, int scrX, int scrY, char currentLine, int x, int y, int y1, int y2){
+ // 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));
+
+ // 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));
+ // if the new Z value is greater than the existing Z value on the buffer, the new pixel is calculated and drawn
+ if(zBuf.getRGB(x, y) == Color.HSBtoRGB(0, 0, 0) ||
+ newZ > zBuf.getRGB(x, y)){ //newZ > zBuf.getRGB(x, y) ||
+ zBuf.setRGB(x, y, newZ);
+ result = new Point3D(0,0,0);
+ //perspectiveMappingMatrix.multiplyPoint3to(new Point3D(x, y, 0), result);
+ // project result
+ result.projectOn(FPDis, scrX, scrY, result2);
+ int colour = texture.getRGB(
+ Math.floorMod((int)(result2.x), texture.getWidth()),
+ Math.floorMod((int)(result2.y), texture.getHeight()));
+ img.setRGB(x, y, (int) (colour - 10*(result2.x/ texture.getWidth()) - 10*(result2.y/ texture.getHeight())));
+ }
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Vector2D.java b/src/main/java/uk/org/floop/epq3d/Vector2D.java
new file mode 100644
index 0000000..319d43b
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Vector2D.java
@@ -0,0 +1,28 @@
+package uk.org.floop.epq3d;
+
+public class Vector2D {
+ // technically not required as vectors have the same information as points, but it's useful to have separate vector and point things..
+ public double x;
+ public double y;
+
+ public Vector2D(double _x, double _y){
+ x = _x;
+ y = _y;
+ }
+ public void createFrom2Points(Point2D start, Point2D end){
+ x = end.x - start.x;
+ y = end.y - start.y;
+ }
+ public double getLength(){
+ return Math.sqrt(x*x + y*y);
+ }
+ public double angleTo(Vector2D vec2){
+ return Math.acos(
+ dot(vec2)
+ / (getLength()*vec2.getLength()))
+ * Math.signum(x*vec2.y - y *vec2.x);
+ }
+ public double dot(Vector2D vec2){
+ return (x*vec2.x) + (y*vec2.y);
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/Vector3D.java b/src/main/java/uk/org/floop/epq3d/Vector3D.java
new file mode 100644
index 0000000..c8e076c
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/Vector3D.java
@@ -0,0 +1,37 @@
+package uk.org.floop.epq3d;// technically not required as vectors have the same information as points, but it's useful to have separate vector and point things..
+
+public class Vector3D {
+ public double x;
+ public double y;
+ public double z;
+
+ public Vector3D(double _x, double _y, double _z){
+ x = _x;
+ y = _y;
+ z = _z;
+ }
+ public void createFrom2Points(Point3D start, Point3D end){
+ x = end.x - start.x;
+ y = end.y - start.y;
+ z = end.z - start.z;
+ }
+ public double getLength(){
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+ public double angleTo(Vector3D vec2){
+ return Math.acos(
+ (x*vec2.x + y*vec2.y + z*vec2.z)
+ /( //-----------------------------------------------------------
+ Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)) *
+ Math.sqrt(Math.pow(vec2.x, 2) + Math.pow(vec2.y, 2) + Math.pow(vec2.z, 2))));
+ }
+ public Vector3D cross(Vector3D vec2){
+ return new Vector3D(
+ y*vec2.z - z*vec2.y,
+ z*vec2.x - x*vec2.z,
+ x*vec2.y - y*vec2.x);
+ }
+ public double dot(Vector3D vec2){
+ return x*vec2.x + y* vec2.y+z* vec2.z;
+ }
+}
diff --git a/src/main/java/uk/org/floop/epq3d/testing.java b/src/main/java/uk/org/floop/epq3d/testing.java
new file mode 100644
index 0000000..05aa248
--- /dev/null
+++ b/src/main/java/uk/org/floop/epq3d/testing.java
@@ -0,0 +1,25 @@
+package uk.org.floop.epq3d;
+
+class testing {
+ public static void main(String[] args) {
+// Matrix matt = new Matrix(2, 2);
+// matt.setItems(new double[][]{
+// {3, 7},
+// {4, 8},
+// });
+// Matrix matt = new Matrix(3, 3);
+// matt.setItems(new double[][]{
+// {2, -1, 3},
+// {0, 5, 2},
+// {1, -1, -2}
+// });
+
+ Matrix matt = new Matrix(4, 4);
+ matt.setItems(new double[][]{
+ {3, 3, 3, 0},
+ {4, 8, 7, 2.3},
+ {5, 1, 3, 9.4},
+ {2, 1, 4, 7}
+ });
+ }
+}