diff --git a/src/Face.java b/src/Face.java new file mode 100644 index 0000000..76596ab --- /dev/null +++ b/src/Face.java @@ -0,0 +1,47 @@ +import java.security.cert.TrustAnchor; +import java.util.ArrayList; + +public class Face { + PointComp[] points; + Vector3D normal; + + public void rotate(Matrix camMatrix){ + for (PointComp point: + points){ + camMatrix.multiplyPoint3to(point.point, point.rotatedPoint); + } + } + public void project(int FPDis, int scrX, int scrY){ + for (PointComp point: + points){ + point.rotatedPoint.projectOn(FPDis, scrX, scrY, point.projectedPoint); + } + } + public void separateTris(){ + + } + 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 = new Vector3D( + vec1.y*vec2.z - vec1.z*vec2.y, + vec1.z*vec2.x - vec1.x*vec2.z, + vec1.x*vec2.y - vec1.y*vec2.x); + } +} diff --git a/src/Line.java b/src/Line.java index 24acda2..5b6138f 100644 --- a/src/Line.java +++ b/src/Line.java @@ -71,12 +71,12 @@ realPoint1 = point2; realPoint2 = point1; } - dx = realPoint2.intX() - realPoint1.intX(); // round all points to the nearest integer + dx = realPoint2.x - realPoint1.x; - dy = realPoint2.intY() - realPoint1.intY(); // the line algorithm only works with integer arithmetic + dy = realPoint2.y - realPoint1.y; sDy = (int) Math.signum(dy); dy = Math.abs(dy); - xy = new Point2D(realPoint1.intX(),realPoint1.intY()); // starting point + xy = new Point2D(realPoint1.x,realPoint1.y); // starting point // check if dy is greater than or less than dx if (dy < dx){ @@ -97,8 +97,8 @@ public int[] nextPix(){ if(!is_initialised){initialise();} //OPTIMIZATION - avoid casting. have a different point class that uses integers instead of float - returnVal[0] = xy.intX(); - returnVal[1] = xy.intY(); + returnVal[0] = xy.x; + returnVal[1] = xy.y; if (iterator=='x' && iteratorVal < dx && iteratorVal != -1){ if (D > 0) { D += 2 * (dy - dx); @@ -119,8 +119,8 @@ } else if(iteratorVal != -1) { iteratorVal = -1; - returnVal[0] = realPoint2.intX(); - returnVal[1] = realPoint2.intY(); + returnVal[0] = realPoint2.x; + returnVal[1] = realPoint2.y; return returnVal; } else { diff --git a/src/Matrix.java b/src/Matrix.java index 3a75712..81cc3b2 100644 --- a/src/Matrix.java +++ b/src/Matrix.java @@ -36,28 +36,24 @@ } return result; } - public Point3D multiplyPoint3(Point3D point) { - double px;double py; double pz; + public void multiplyPoint3to(Point3D point, Point3D result) { if(x==3){ - px = point.x * item(0,0) + point.y*item(1,0) + point.z*item(2,0); - py = point.x * item(0,1) + point.y*item(1,1) + point.z*item(2,1); - pz = point.x * item(0,2) + point.y*item(1,2) + point.z*item(2,2); + result.x = point.x * item(0,0) + point.y*item(1,0) + point.z*item(2,0); + result.y = point.x * item(0,1) + point.y*item(1,1) + point.z*item(2,1); + result.z = point.x * item(0,2) + point.y*item(1,2) + point.z*item(2,2); } else if(x == 4){ - px = point.x * item(0,0) + point.y*item(1,0) + point.z*item(2,0) + item(3,0); - py = point.x * item(0,1) + point.y*item(1,1) + point.z*item(2,1) + item(3,1); - pz = point.x * item(0,2) + point.y*item(1,2) + point.z*item(2,2) + item(3,2); + result.x = point.x * item(0,0) + point.y*item(1,0) + point.z*item(2,0) + item(3,0); + result.y = point.x * item(0,1) + point.y*item(1,1) + point.z*item(2,1) + item(3,1); + result.z = point.x * item(0,2) + point.y*item(1,2) + point.z*item(2,2) + item(3,2); } else {throw new RuntimeException("wrong-dimensions");} - return new Point3D(px, py, pz); } - public Point2D multiplyPoint2(Point3D point) throws Exception { - double px;double py; + public void multiplyPoint2to(Point2D point, Point2D result) throws Exception { if(x==2){ - px = point.x * item(0,0) + point.y*item(0,1); - py = point.x * item(1,0) + point.y*item(1,1); + result.x = (int)(point.x * item(0,0) + point.y*item(0,1)); + result.y = (int)(point.x * item(1,0) + point.y*item(1,1)); } else if(x == 3){ - px = point.x * item(0,0) + point.y*item(0,1) + item(0,2); - py = point.x * item(1,0) + point.y*item(1,1) + item(1,2); + result.x = (int)(point.x * item(0,0) + point.y*item(0,1) + item(0,2)); + result.y = (int)(point.x * item(1,0) + point.y*item(1,1) + item(1,2)); } else {throw new Exception("wrong-dimensions");} - return new Point2D(px, py); } } diff --git a/src/Object3d.java b/src/Object3d.java new file mode 100644 index 0000000..ef26e52 --- /dev/null +++ b/src/Object3d.java @@ -0,0 +1,48 @@ +public class Object3d { + public PointComp[] points; + public int[][] faceList; + public Face[] faces; + public Point3D boundingSphereC; + public double boundingSphereR; + + public Object3d(PointComp[] _points, int[][] faceList, boolean initialise){ + points = _points; + if(initialise){initialise();} + } + public void initialise(){ + // init faces + faces = new Face[faceList.length]; + for (int face = 0; face < faceList.length; face+= 1) { + faces[face] = new Face(); + faces[face].points = new PointComp[faceList[face].length]; + for (int point = 0; point < faceList[face].length; point += 1){ + faces[face].points[point] = points[faceList[face][point]]; + } + } + // init bounding sphere + double distance = 0; + double newDis; + Point3D pointA = points[0].point; + Point3D pointB = points[0].point; + // todo - maybe use some vector classes? + for (int i = 1; i < points.length; i+=1) { + newDis = Math.pow(points[0].point.x - points[i].point.x, 2) + + Math.pow(points[0].point.y - points[i].point.y, 2) + + Math.pow(points[0].point.z - points[i].point.z, 2); + if (newDis >= 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.y + pointB.y)/2); + boundingSphereR = Math.sqrt(distance)/2; + } +} diff --git a/src/ObjectCollection.java b/src/ObjectCollection.java new file mode 100644 index 0000000..78dfdec --- /dev/null +++ b/src/ObjectCollection.java @@ -0,0 +1,2 @@ +public class ObjectCollection { +} diff --git a/src/Player.java b/src/Player.java index 254d203..6fdbd6d 100644 --- a/src/Player.java +++ b/src/Player.java @@ -1,8 +1,5 @@ -import sun.awt.motif.X11CNS11643; - import java.awt.*; import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; @@ -12,13 +9,15 @@ private BufferedImage image; // current position of the player on the board grid public Matrix camMatrix; - public double Fpdis = 0.4; + public double FOV = 100; + protected double Fpdis; private Point3D position = new Point3D(-30,0,0); private Point3D rotation = new Point3D(0,Math.PI / 2, 0); private Point3D direction = new Point3D(0,0,0); public double mouseSensitivity = 0.005; // radians per pixel // called when the player is initialised public Player() { + Fpdis = Math.tanh(Math.toRadians(FOV)/2d); } // unused because the player should not be drawn @@ -39,40 +38,52 @@ } public void keyPressed(int key) { - if (key == KeyEvent.VK_W) { - direction.x = 1; - } else if (key == KeyEvent.VK_S) { - direction.x = -1; - } else if (key == KeyEvent.VK_A) { - direction.y = -1; - } else if (key == KeyEvent.VK_D) { - direction.y = 1; - } else if (key == KeyEvent.VK_SPACE) { - direction.z = 1; - } else if (key == KeyEvent.VK_SHIFT) { - direction.z = -1; + 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_D && direction.y < 1) { + direction.y += 1; + } else if (key == KeyEvent.VK_A && -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 = 0; - } else if (key == KeyEvent.VK_S) { - direction.x = 0; - } else if (key == KeyEvent.VK_A) { - direction.y = 0; - } else if (key == KeyEvent.VK_D) { - direction.y = 0; - } else if (key == KeyEvent.VK_SPACE) { - direction.z = 0; - } else if (key == KeyEvent.VK_SHIFT) { - direction.z = 0; + 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_D && direction.y > -1) { + direction.y -= 1; + } else if (key == KeyEvent.VK_A && -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; + } // calculate direction vector to translate position by Matrix zMat = new Matrix(3, 3); zMat.setItems(new double[][]{ @@ -92,15 +103,17 @@ {0, Math.cos(rotation.x), Math.sin(rotation.x)}, {0, -Math.sin(rotation.x), Math.cos(rotation.x)}} ); + // calculate inverse Z mat for player movement 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}} ); - // apply zmat to direction vector + // apply izMat to direction vector try { - Point3D dirvec = izMat.multiplyPoint3(direction); + Point3D dirvec = new Point3D(0,0,0); + izMat.multiplyPoint3to(direction, dirvec); position.translate(dirvec); } catch (Exception e) { throw new RuntimeException(e); @@ -112,17 +125,7 @@ {0, 0, 1, -position.z}} ); // multiply out camera matrix - try { - //camMatrix = ((zMat.multiply(yMat)).multiply(xMat)).multiply(traMat); -// camMatrix = zMat.multiply(yMat); -// camMatrix = camMatrix.multiply(xMat); -// camMatrix = camMatrix.multiply(traMat); - camMatrix = xMat.multiply(yMat); - camMatrix = camMatrix.multiply(zMat); - camMatrix = camMatrix.multiply(traMat); - } catch (Exception e) { - throw new RuntimeException(e); - } + camMatrix = xMat.multiply(yMat).multiply(zMat).multiply(traMat); } // returnValue Functions diff --git a/src/Point2D.java b/src/Point2D.java index 7561047..ab17c36 100644 --- a/src/Point2D.java +++ b/src/Point2D.java @@ -1,21 +1,21 @@ import java.util.ArrayList; public class Point2D { - public double x; - public double y; - public Point2D(double _x, double _y){ + public int x; + public int y; + public Point2D(int _x, int _y){ x = _x; y = _y; } - public void set(double _x, double _y){ + public void set(int _x, int _y){ x = _x; y = _y; } - public double[] get(){ - return new double[]{x, y}; + public int[] get(){ + return new int[]{x, y}; } - public int intX(){return (int)Math.round(x);} - public int intY(){return (int)Math.round(y);} +// public int intX(){return (int)Math.round(x);} +// public int intY(){return (int)Math.round(y);} } diff --git a/src/Point3D.java b/src/Point3D.java index e45488b..7bba064 100644 --- a/src/Point3D.java +++ b/src/Point3D.java @@ -20,10 +20,16 @@ } public Point2D project(double fpdis, int scrX, int scrY){ return new Point2D( - (((fpdis*y)/z + .5)*scrX), - (((fpdis*x)/z + .5)*scrY) + (int)(((fpdis*y)/z + .5)*scrX), + (int)(((fpdis*x)/z + .5)*scrX) + // multiply by scrX both times such that the projected screen always projects points between -1 newPoints = new ArrayList(); for (Point3D point: points) { - Point3D _new = player.camMatrix.multiplyPoint3((point)); + Point3D _new = new Point3D(0,0,0); + player.camMatrix.multiplyPoint3to(point, _new); if(_new.z > .1) { newPoints.add(_new); } else{ @@ -140,6 +139,7 @@ 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); } @@ -149,8 +149,8 @@ // 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 / 2d + mouseRel.x, - MouseInfo.getPointerInfo().getLocation().y - getLocationOnScreen().y - getSize().height / 2d + mouseRel.y); + 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 { @@ -170,6 +170,7 @@ public void mouseClicked(MouseEvent mouseEvent) { if (mouseEvent.getButton() == MouseEvent.BUTTON1){ captured = true; + get_mouse_rel(); setCursor(invisibleCursor); } } diff --git a/src/Triangle.java b/src/Triangle.java index 7c85ed3..4b79abf 100644 --- a/src/Triangle.java +++ b/src/Triangle.java @@ -34,7 +34,7 @@ LineLong.draw(img); LineA.draw(img); LineB.draw(img); - for(int x = min.intX(); x <= max.intX(); x += 1) { + for(int x = min.x; x <= max.x; x += 1) { while(lastXLong == point1[0]) { point1 = LineLong.nextPix(); } @@ -54,13 +54,14 @@ lastX = point2[0]; if(point1[1] < point2[1]) { for (int y = point1[1]; y <= point2[1]; y += 1) { - try{img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1));} - catch(ArrayIndexOutOfBoundsException ignored){} + if(x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) { // check if drawing in bounds + img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1)); + } }} else { for (int y = point2[1]; y <= point1[1]; y += 1) { - try{img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1));} - catch(ArrayIndexOutOfBoundsException ignored){} - + if(x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) { // check if drawing in bounds + img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1)); + } }} } is_initialised = false; diff --git a/src/Vector3D.java b/src/Vector3D.java new file mode 100644 index 0000000..55457f4 --- /dev/null +++ b/src/Vector3D.java @@ -0,0 +1,20 @@ +// 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 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)))); + } +}