diff --git a/out/production/EPQ 3D renderer/App.class b/out/production/EPQ 3D renderer/App.class index 488c832..a13221e 100644 --- a/out/production/EPQ 3D renderer/App.class +++ b/out/production/EPQ 3D renderer/App.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Face.class b/out/production/EPQ 3D renderer/Face.class index b38f9a6..54b5092 100644 --- a/out/production/EPQ 3D renderer/Face.class +++ b/out/production/EPQ 3D renderer/Face.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Matrix.class b/out/production/EPQ 3D renderer/Matrix.class index c8eecf9..698f676 100644 --- a/out/production/EPQ 3D renderer/Matrix.class +++ b/out/production/EPQ 3D renderer/Matrix.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Object3d.class b/out/production/EPQ 3D renderer/Object3d.class index 7724b1a..694bbc1 100644 --- a/out/production/EPQ 3D renderer/Object3d.class +++ b/out/production/EPQ 3D renderer/Object3d.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Player.class b/out/production/EPQ 3D renderer/Player.class index 8b9f9c0..b8bf0db 100644 --- a/out/production/EPQ 3D renderer/Player.class +++ b/out/production/EPQ 3D renderer/Player.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Screen.class b/out/production/EPQ 3D renderer/Screen.class index bcf7d88..6d7f20a 100644 --- a/out/production/EPQ 3D renderer/Screen.class +++ b/out/production/EPQ 3D renderer/Screen.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Triangle.class b/out/production/EPQ 3D renderer/Triangle.class index 2cd17e2..5a043b9 100644 --- a/out/production/EPQ 3D renderer/Triangle.class +++ b/out/production/EPQ 3D renderer/Triangle.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Vector3D.class b/out/production/EPQ 3D renderer/Vector3D.class index c8090a9..79370e5 100644 --- a/out/production/EPQ 3D renderer/Vector3D.class +++ b/out/production/EPQ 3D renderer/Vector3D.class Binary files differ diff --git a/src/App.java b/src/App.java index bf5ec33..a2131d2 100644 --- a/src/App.java +++ b/src/App.java @@ -37,23 +37,46 @@ public static void initObjects(){ mainCollection = new ObjectCollection(); +// 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(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}, +// }, 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(-10,-10,-10), - new PointComp(-10,-10,10), - new PointComp(-10,10,-10), - new PointComp(-10,10,10), - new PointComp(10,-10,-10), - new PointComp(10,-10,10), - new PointComp(10,10,-10), - new PointComp(10,10,10) - + new PointComp(0,0,0), + new PointComp(20,0,0), + new PointComp(0,10,0), + new PointComp(0,0,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}, + {0,1,2}, + {0,2,3}, + {0,3,1} }, true)); } } diff --git a/src/Face.java b/src/Face.java index d027d53..2ad549d 100644 --- a/src/Face.java +++ b/src/Face.java @@ -28,7 +28,7 @@ point.setRotatedPoint(camMatrix); point.setProjectedPoint(FPDis, scrX, scrY); // skip rendering if any points are behind the camera - if(point.getRotatedPoint().z < 0){ + if(point.getRotatedPoint().z < 0.1){ valid = false; break; } @@ -68,9 +68,6 @@ 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); + normal = vec1.cross(vec2); } } diff --git a/src/Matrix.java b/src/Matrix.java index 81cc3b2..7202d29 100644 --- a/src/Matrix.java +++ b/src/Matrix.java @@ -7,7 +7,7 @@ x = _x; y= _y; items = new double[y][x]; } - public double item(int _x, int _y){ + public double getItem(int _x, int _y){ return items[_y][_x]; } public void setItem(int _x, int _y, double val){ @@ -27,7 +27,7 @@ for(int ry = 0; ry< result.y; ry+=1){ newItem = 0; for(int i = 0; i boundingSphereR){ + boundingSphereR = distance; + } + } } } diff --git a/src/ObjectCollection.java b/src/ObjectCollection.java index efe60cc..fb9de3f 100644 --- a/src/ObjectCollection.java +++ b/src/ObjectCollection.java @@ -11,7 +11,7 @@ public ArrayList objects = new ArrayList(); public void invalidate(boolean invalidatePoints){ - // the first level of object collections should contain all the points for the sub-levels. + // 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) { @@ -27,16 +27,26 @@ } } - public void draw(BufferedImage img, BufferedImage debugimg, Matrix camMatrix, double FPDis, int scrX, int scrY, Point3D playerPos){ - // todo check for frustum culling + public void draw(BufferedImage img, BufferedImage debugImg, Matrix camMatrix, Point3D playerPos, Plane[] frustumPlanes, double FPDis, int scrX, int scrY){ for (Object3d object: objects) { - object.draw(img, debugimg, camMatrix, FPDis, scrX, scrY, playerPos); + 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, debugImg, camMatrix, FPDis, scrX, scrY, playerPos);} } // todo check for frustum culling for(ObjectCollection collection: subCollections){ - collection.draw(img, debugimg, camMatrix, FPDis, scrX, scrY, playerPos); + collection.draw(img, debugImg, camMatrix, playerPos, frustumPlanes, FPDis, scrX, scrY); } } public void addCollection(int[] pointList){ diff --git a/src/Plane.java b/src/Plane.java new file mode 100644 index 0000000..fa525cf --- /dev/null +++ b/src/Plane.java @@ -0,0 +1,27 @@ +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/Player.java b/src/Player.java index 25215e5..cb16b47 100644 --- a/src/Player.java +++ b/src/Player.java @@ -5,20 +5,35 @@ 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; - private Point3D position = new Point3D(-30,0,0); + 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() { - Fpdis = Math.tanh(Math.toRadians(FOV)/2d); + 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 @@ -43,9 +58,9 @@ direction.x += 1; } else if (key == KeyEvent.VK_S && -direction.x < 1) { direction.x += -1; - } else if (key == KeyEvent.VK_D && direction.y < 1) { + } else if (key == KeyEvent.VK_A && direction.y < 1) { direction.y += 1; - } else if (key == KeyEvent.VK_A && -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; @@ -58,9 +73,9 @@ direction.x -= 1; } else if (key == KeyEvent.VK_S && -direction.x > -1) { direction.x -= -1; - } else if (key == KeyEvent.VK_D && direction.y > -1) { + } else if (key == KeyEvent.VK_A && direction.y > -1) { direction.y -= 1; - } else if (key == KeyEvent.VK_A && -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; @@ -71,7 +86,7 @@ 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; + rotation.z -= mouseRel.x * mouseSensitivity; // force z rotation to wrap around if(rotation.z<-Math.PI){ rotation.z = Math.PI; @@ -79,13 +94,13 @@ rotation.z = -Math.PI; } rotation.y += mouseRel.y * mouseSensitivity; - // max out y rotation at 0 and 90 degrees + // 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 + // define rotation and translation matrices Matrix zMat = new Matrix(3, 3); zMat.setItems(new double[][]{ {Math.cos(rotation.z), Math.sin(rotation.z), 0}, @@ -104,13 +119,25 @@ {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 + // 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); @@ -125,15 +152,64 @@ {0, 1, 0, -position.y}, {0, 0, 1, -position.z}} ); - // multiply out camera matrix for rotation - camMatrix = xMat.multiply(yMat).multiply(zMat); - // calculate view vector - /*Point3D viewPoint = new Point3D(0,0,0); - camMatrix.multiplyPoint3to(new Point3D(0,0,1), viewPoint); - viewVector.x = viewPoint.x;viewVector.y=viewPoint.y;viewVector.z=viewPoint.z;*/ + // multiply out matrices + rotMatrix = xMat.multiply(yMat).multiply(zMat); + camMatrix = rotMatrix.multiply(traMat); + invRotMatrix = izMat.multiply(iyMat).multiply(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)); + } - // add translation to camMatrix - camMatrix = camMatrix.multiply(traMat); + // 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 diff --git a/src/Point3D.java b/src/Point3D.java index 595db2d..81ef356 100644 --- a/src/Point3D.java +++ b/src/Point3D.java @@ -20,15 +20,17 @@ } public Point2D project(double fpdis, int scrX, int scrY){ return new Point2D( - (int)(((fpdis*y)/z + .5)*scrX), - (int)(((fpdis*x)/z + .5)*scrY) + (int)(((fpdis*y)/(z) + .5)*scrX), + (int)(((fpdis*x)/(z) + .5)*scrY) // multiply by scrX both times such that the projected screen always projects points between -1