diff --git a/out/production/EPQ 3D renderer/App.class b/out/production/EPQ 3D renderer/App.class index b3340a9..488c832 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 0c680d6..b38f9a6 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/Line.class b/out/production/EPQ 3D renderer/Line.class index 44c1219..20f60ad 100644 --- a/out/production/EPQ 3D renderer/Line.class +++ b/out/production/EPQ 3D renderer/Line.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Object3d.class b/out/production/EPQ 3D renderer/Object3d.class index ed93e02..7724b1a 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 91430bb..8b9f9c0 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/Point3D.class b/out/production/EPQ 3D renderer/Point3D.class index 9c24a62..bb70794 100644 --- a/out/production/EPQ 3D renderer/Point3D.class +++ b/out/production/EPQ 3D renderer/Point3D.class Binary files differ diff --git a/out/production/EPQ 3D renderer/PointComp.class b/out/production/EPQ 3D renderer/PointComp.class index 0241e0c..e1aaa8f 100644 --- a/out/production/EPQ 3D renderer/PointComp.class +++ b/out/production/EPQ 3D renderer/PointComp.class Binary files differ diff --git a/out/production/EPQ 3D renderer/Screen.class b/out/production/EPQ 3D renderer/Screen.class index 2b6e860..bcf7d88 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 991bbe8..2cd17e2 100644 --- a/out/production/EPQ 3D renderer/Triangle.class +++ b/out/production/EPQ 3D renderer/Triangle.class Binary files differ diff --git a/src/App.java b/src/App.java index 1053058..bf5ec33 100644 --- a/src/App.java +++ b/src/App.java @@ -1,7 +1,7 @@ import javax.swing.*; 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"); @@ -9,7 +9,8 @@ window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // create the jpanel to draw on. // this also initializes the game loop - Screen screen = new Screen(); + initObjects(); + Screen screen = new Screen(mainCollection); // add the jpanel to the window window.add(screen); window.addKeyListener(screen); @@ -33,4 +34,26 @@ // just know that when main runs it will call initWindow() once. SwingUtilities.invokeLater(App::initWindow); } + + 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)); + } } diff --git a/src/Face.java b/src/Face.java index 76596ab..d027d53 100644 --- a/src/Face.java +++ b/src/Face.java @@ -1,24 +1,53 @@ +import java.awt.image.BufferedImage; import java.security.cert.TrustAnchor; import java.util.ArrayList; public class Face { PointComp[] points; Vector3D normal; + Triangle[] tris; - public void rotate(Matrix camMatrix){ - for (PointComp point: - points){ - camMatrix.multiplyPoint3to(point.point, point.rotatedPoint); + public void initialise(){ + calculateNormal(); + separateTris(); + } + + public void invalidate(){ + for (Triangle tri: + tris) { + tri.invalidate(); } } - public void project(int FPDis, int scrX, int scrY){ + public int draw(BufferedImage img, BufferedImage debugImg, Matrix camMatrix, double FPDis, int scrX, int scrY){ + // check for backface culling has been done previously. + // initialise points + int numberofPixels = 0; + boolean valid = true; for (PointComp point: - points){ - point.rotatedPoint.projectOn(FPDis, scrX, scrY, point.projectedPoint); + points) { + point.setRotatedPoint(camMatrix); + point.setProjectedPoint(FPDis, scrX, scrY); + // skip rendering if any points are behind the camera + if(point.getRotatedPoint().z < 0){ + valid = false; + break; + } } + if(valid){for (Triangle tri: + tris) { + numberofPixels += tri.draw(img); + }} + return numberofPixels; } 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()); + } + tris = newTris; } public void calculateNormal(){ // too many new variables diff --git a/src/Line.java b/src/Line.java index 5b6138f..3184881 100644 --- a/src/Line.java +++ b/src/Line.java @@ -27,36 +27,29 @@ * @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(); - try { + 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)); } - catch (ArrayIndexOutOfBoundsException ignored){ - // System.out.println(returnVal[0] + " " + returnVal[1]); - } + } } else { for (int i = 0; i <= dy; i++){ nextPix(); - try { + 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)); } - catch (ArrayIndexOutOfBoundsException ignored){ - // System.out.println(returnVal[0] + " " + returnVal[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()); */ - - is_initialised = false; } /** diff --git a/src/Object3d.java b/src/Object3d.java index ef26e52..976859a 100644 --- a/src/Object3d.java +++ b/src/Object3d.java @@ -1,3 +1,6 @@ +import java.awt.*; +import java.awt.image.BufferedImage; + public class Object3d { public PointComp[] points; public int[][] faceList; @@ -5,10 +8,39 @@ public Point3D boundingSphereC; public double boundingSphereR; - public Object3d(PointComp[] _points, int[][] faceList, boolean initialise){ + public Object3d(PointComp[] _points, int[][] _faceList, boolean initialise){ points = _points; + faceList = _faceList; if(initialise){initialise();} } + public void invalidate(){ + for (Face face: + faces) { + face.invalidate(); + } + } + public void draw(BufferedImage img, BufferedImage debugimg, Matrix camMatrix, double FPDis, int scrX, int scrY, Point3D playerPos){ + int iterator = 0; + for (Face face: + faces) { + // check whether faces are pointing towards or away from the camera + // TODO figure out angle + Vector3D camVec = new Vector3D( + face.points[0].point.x - playerPos.x, + face.points[0].point.y - playerPos.y, + face.points[0].point.z - playerPos.z + ); + double ang = face.normal.angleTo(camVec); + //debugimg.getGraphics().drawString(Math.round(Math.toDegrees(ang)) + " angle " + iterator, 10, 20 + 10*iterator); + int numberOfPixels = 0; + if(ang <= Math.PI/2-0.01){ + numberOfPixels = face.draw(img, debugimg, camMatrix, FPDis, scrX, scrY); + } + debugimg.getGraphics().drawString(iterator + ": " +numberOfPixels , 10, 20 + 10*iterator); + + iterator += 1; + } + } public void initialise(){ // init faces faces = new Face[faceList.length]; @@ -18,6 +50,7 @@ for (int point = 0; point < faceList[face].length; point += 1){ faces[face].points[point] = points[faceList[face][point]]; } + faces[face].initialise(); } // init bounding sphere double distance = 0; diff --git a/src/ObjectCollection.java b/src/ObjectCollection.java index 78dfdec..efe60cc 100644 --- a/src/ObjectCollection.java +++ b/src/ObjectCollection.java @@ -1,2 +1,67 @@ +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 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 sub-levels. + // 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 debugimg, Matrix camMatrix, double FPDis, int scrX, int scrY, Point3D playerPos){ + // todo check for frustum culling + for (Object3d object: + objects) { + 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); + } + } + 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); + } + } + } + // } diff --git a/src/Player.java b/src/Player.java index 6fdbd6d..25215e5 100644 --- a/src/Player.java +++ b/src/Player.java @@ -14,6 +14,7 @@ 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 Vector3D viewVector = new Vector3D(0,0,0); public double mouseSensitivity = 0.005; // radians per pixel // called when the player is initialised public Player() { @@ -124,8 +125,15 @@ {0, 1, 0, -position.y}, {0, 0, 1, -position.z}} ); - // multiply out camera matrix - camMatrix = xMat.multiply(yMat).multiply(zMat).multiply(traMat); + // 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;*/ + + // add translation to camMatrix + camMatrix = camMatrix.multiply(traMat); } // returnValue Functions diff --git a/src/Point3D.java b/src/Point3D.java index 7bba064..595db2d 100644 --- a/src/Point3D.java +++ b/src/Point3D.java @@ -21,14 +21,14 @@ public Point2D project(double fpdis, int scrX, int scrY){ return new Point2D( (int)(((fpdis*y)/z + .5)*scrX), - (int)(((fpdis*x)/z + .5)*scrX) + (int)(((fpdis*x)/z + .5)*scrY) // multiply by scrX both times such that the projected screen always projects points between -1 newPoints = new ArrayList(); + /* ArrayList newPoints = new ArrayList(); for (Point3D point: points) { Point3D _new = new Point3D(0,0,0); player.camMatrix.multiplyPoint3to(point, _new); @@ -115,8 +121,14 @@ try{t1.draw(img);}catch (NullPointerException ignored){} try{t2.draw(img);}catch (NullPointerException ignored){} - ang += 0.02; + ang += 0.02;*/ + mainCollection.invalidate(true); + mainCollection.draw(img, debugimg, player.camMatrix, player.Fpdis, getWidth(), getHeight(), player.getPos()); g.drawImage(img, 0, 0, this); + g.drawImage(debugimg, 0, 0, this); + // DEBUG DRAWING + g.drawString(Math.round(1000/(float)(System.currentTimeMillis() - lastTime)) + " fps" , 10, 10); + lastTime = System.currentTimeMillis(); } diff --git a/src/Triangle.java b/src/Triangle.java index 4b79abf..77dbada 100644 --- a/src/Triangle.java +++ b/src/Triangle.java @@ -15,30 +15,33 @@ private Point2D max; // progress variables - + public void invalidate() { + is_initialised = false; + } public Triangle(Point2D _pA, Point2D _pB, Point2D _pC){ point1 = _pA; point2 = _pB; point3 = _pC; } - public void draw(BufferedImage img){ + // returns int for debug + public int draw(BufferedImage img){ + int numberOfPixels = 0; // debug + long lastMillis; + if (!is_initialised){initialise();} - int[] point1 = new int[]{0, 0}; - int[] point2 = new int[]{0, 0}; - int lastX = 0; - int lastXLong = 0; + int[] point1; + int[] point2; char currentLine = 'A'; - LineLong.initialise(); - LineA.initialise(); - LineB.initialise(); - LineLong.draw(img); - LineA.draw(img); - LineB.draw(img); - for(int x = min.x; x <= max.x; x += 1) { - while(lastXLong == point1[0]) { + + 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]) { point1 = LineLong.nextPix(); } - while(lastX == point2[0]) { + while(x-1 == point2[0]) { if (currentLine == 'A') { try{point2 = LineA.nextPix();} catch (RuntimeException e){ @@ -50,21 +53,24 @@ point2 = LineB.nextPix(); } } - lastXLong = point1[0]; - lastX = point2[0]; - if(point1[1] < point2[1]) { - for (int y = point1[1]; y <= point2[1]; y += 1) { - if(x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) { // check if drawing in bounds + // 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) { img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1)); } - }} else { - for (int y = point2[1]; y <= point1[1]; y += 1) { - if(x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) { // check if drawing in bounds + } else { + for (int y = Math.max(point2[1], 0); y <= Math.min(point1[1], img.getHeight() - 1); y += 1) { img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1)); } - }} + } + } } - is_initialised = false; + lastMillis = (System.currentTimeMillis() - lastMillis); + return (int)lastMillis; } public void initialise(){ if (point1 == null || point2 == null || point3 == null){ @@ -73,7 +79,7 @@ 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. - // if there are no vertical lines, then we need to find which points touch the edges. + // we need to figure out which points touch the edges in order to find which line is the 'full length' edge. Point2D realPointA; Point2D realPointB; Point2D realPointC;