diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f144601
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,28 @@
+
+ 4.0.0
+ uk.org.floop
+ epq-3d
+ 1.0-SNAPSHOT
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 11
+
+
+
+
+ EPQ 3D Renderer
+ https://www.floop.org.uk/cory/epq/3d
+
+
+ com.googlecode.json-simple
+ json-simple
+ 1.1
+
+
+
diff --git a/src/main/java/uk/org/floop/epq3d/App.java b/src/main/java/uk/org/floop/epq3d/App.java
index 99173c4..1219fc8 100644
--- a/src/main/java/uk/org/floop/epq3d/App.java
+++ b/src/main/java/uk/org/floop/epq3d/App.java
@@ -1,10 +1,7 @@
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;
+import java.awt.*;
class App {
static ObjectCollection mainCollection;
@@ -31,6 +28,7 @@
window.setLocationRelativeTo(null);
// display the window
window.setVisible(true);
+ long i = 2147483648L;
}
public static void main(String[] args) {
@@ -44,13 +42,21 @@
public static void initObjects(){
mainCollection = new ObjectCollection();
- BufferedImage testTexture;
- try {
+ Texture 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"));
+ testTexture = new Texture(ImageIO.read(new File("/home/cory/Screenshot from 2022-09-26 13-05-40.png")));
} catch (IOException e) {
throw new RuntimeException(e);
- }
+ }*/
+ Texture red = new Texture(Color.red);
+ Texture green = new Texture(Color.GREEN);
+ Texture blue = new Texture(Color.BLUE);
+ Texture cyan = new Texture(Color.CYAN);
+ Texture magenta = new Texture(Color.magenta);
+ Texture yellow = new Texture(Color.yellow);
+
+
mainCollection.addObject(new Object3d(new PointComp[]{
new PointComp(-30,-10,-10),
new PointComp(-30,-10,10),
@@ -77,8 +83,8 @@
{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,
+ false,
+ new Texture[]{red, green, blue, cyan, magenta, yellow},
true));
// mainCollection.addObject(new Object3d(new PointComp[]{
// new PointComp(-10,10,10),
@@ -108,8 +114,8 @@
{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,
+ false,
+ new Texture[]{red, blue, cyan, yellow},
true));
}
}
diff --git a/src/main/java/uk/org/floop/epq3d/Face.java b/src/main/java/uk/org/floop/epq3d/Face.java
index 426c6cd..120ece6 100644
--- a/src/main/java/uk/org/floop/epq3d/Face.java
+++ b/src/main/java/uk/org/floop/epq3d/Face.java
@@ -8,24 +8,18 @@
public Point2D[] UVPoints;
public Vector3D normal;
public Triangle[] tris;
+ public int[][] trisFaceList;
public boolean hasEdges;
- public BufferedImage texture;
- public Matrix[] perspectiveMappingMatrices = new Matrix[]{null, null, null, null, null, null};
+ public Texture texture;
+ public Matrix[][] perspectiveMappingMatrices;
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();
+ perspectiveMappingMatrices = new Matrix[tris.length][2];
+ generateUVMatrices();
// fixedFace is a constant object which is used temporarily when the face intersects with the camera,
// and thus needs to be sliced.
fixedFace = new Face();
@@ -53,7 +47,8 @@
// initialise points
int numberOfPixels = 0;
boolean valid = applyPointTransforms(camMatrix, FPDis, scrX, scrY);
- //bakePerspectiveMatrices();
+ // this function is completed every frame, without checking whether pixels will be drawn first
+ bakePerspectiveMatrices(img, zBuf, debugImg, camMatrix, FPDis, scrX, scrY);
if (valid) {
drawTris(img, zBuf, FPDis, scrX, scrY);}
@@ -121,6 +116,7 @@
}
return numberOfPixels;
}
+
public void drawTris(BufferedImage img, BufferedImage zBuf, double FPDis, int scrX, int scrY) {
for (Triangle tri :
tris) {
@@ -143,7 +139,13 @@
}
public void separateTris(){
Triangle[] newTris = new Triangle[points.length - 2];
+ int[][] newTrisFaceList = new int[points.length - 2][3];
+
for(int i = 0; i< newTris.length; i+=1){
+ newTrisFaceList[i][0] = i;
+ newTrisFaceList[i][1] = i+1;
+ newTrisFaceList[i][2] = i+2;
+
newTris[i] = new Triangle(
points[0].getProjectedPoint(),
points[i+1].getProjectedPoint(),
@@ -152,6 +154,7 @@
texture, perspectiveMappingMatrices[i]);
}
tris = newTris;
+ trisFaceList = newTrisFaceList;
}
public void calculateNormal(){
// too many new variables
@@ -174,6 +177,226 @@
if(!valid){throw new RuntimeException("Could not calculate normal of face");}
normal = vec1.cross(vec2);
}
+
+ private void generateUVMatrices() {
+ // temporary object for multiplying
+ Matrix MultMat = new Matrix(4, 4);
+ // temporary unit up vector for reference
+ Vector2D vFin = new Vector2D(0, 1);
+ // temporary vector representing a vector between two points on the uv / triangle
+ Vector2D vOrig = new Vector2D(0,0);
+
+ // repeat for every triangle (this method must be called BEFORE separateTris is called)
+ int i = 0;
+ for (int[] tri: trisFaceList) {
+ // generate two matrices -
+ // uvTo00 takes the uv's point 1 to 0,0 with its point 2 at 0,1 (rotated, translated and scaled)
+ Matrix uvTo00 = new Matrix(4, 4);
+ uvTo00.setItems(new double[][]{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
+ {
+ vOrig.createFrom2Points(UVPoints[tri[0]], UVPoints[tri[1]]);
+ double rotAng = -vOrig.angleTo(vFin);
+ // set to rotation
+ MultMat.setItems(new double[][]{
+ {Math.cos(rotAng), Math.sin(rotAng),0,0},
+ {-Math.sin(rotAng),Math.cos(rotAng),0,0},
+ {0,0,1,0},
+ {0,0,0,1},
+ });
+ uvTo00.multiply(MultMat);
+
+ double scaleFac = 1/ vOrig.getLength();
+ // set to scaling
+ MultMat.setItems(new double[][]{
+ {scaleFac,0,0,0},
+ {0,scaleFac,0,0},
+ {0,0,scaleFac,0},
+ {0,0,0,1},
+ });
+ uvTo00.multiply(MultMat);
+ // set to translation
+ MultMat.setItems(new double[][]{
+ {1,0,0,-UVPoints[tri[0]].x},
+ {0,1,0,-UVPoints[tri[0]].y},
+ {0,0,1,0},
+ {0,0,0,1},
+ });
+ uvTo00.multiply(MultMat);
+
+ }
+ // faceTo00 takes the face's point 1 to 0,0 with its point 2 at 1,0 (rotated, translated and scale
+ Matrix faceTo00 = new Matrix(4, 4);
+ faceTo00.setItems(new double[][]{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
+ {
+ Vector3D rotVector301 = new Vector3D();
+ Vector3D rotVector302 = new Vector3D();
+
+ // rotation (pain)
+ {
+ double rotAng;
+ rotVector301.createFrom2Points(points[tri[0]].point, points[tri[1]].point);
+ rotVector302.createFrom2Points(points[tri[0]].point, points[tri[2]].point);
+
+ vOrig.x = rotVector301.x;
+ vOrig.y = rotVector301.y;
+ rotAng = vFin.angleTo(vOrig);
+ MultMat.setItems(new double[][]{
+ { Math.cos(rotAng), Math.sin(rotAng), 0, 0},
+ {-Math.sin(rotAng), Math.cos(rotAng), 0, 0},
+ { 0, 0, 1, 0},
+ { 0, 0, 0, 1},
+ });
+ faceTo00 = MultMat.multiplyGetResult(faceTo00);
+ faceTo00.multiplyVec3to(rotVector301, rotVector301);
+
+ vOrig.x = rotVector301.z;
+ vOrig.y = rotVector301.y;
+ rotAng = -vFin.angleTo(vOrig);
+ MultMat.setItems(new double[][]{
+ {1, 0, 0, 0},
+ {0, Math.cos(rotAng), Math.sin(rotAng), 0},
+ {0, -Math.sin(rotAng), Math.cos(rotAng), 0},
+ {0, 0, 0, 1},
+ });
+
+ faceTo00 = MultMat.multiplyGetResult(faceTo00);
+ faceTo00.multiplyVec3to(rotVector302, rotVector302);
+
+ Vector2D vFin2 = new Vector2D(1, 0);
+ vOrig.x = rotVector302.x;
+ vOrig.y = rotVector302.z;
+ rotAng = -vFin2.angleTo(vOrig);
+ MultMat.setItems(new double[][]{
+ {Math.cos(rotAng), 0, -Math.sin(rotAng), 0},
+ {0, 1, 0, 0},
+ {Math.sin(rotAng), 0, Math.cos(rotAng), 0},
+ {0, 0, 0, 1},
+ });
+ faceTo00 = MultMat.multiplyGetResult(faceTo00);
+ }
+ // scale and translation (less pain)
+ rotVector301.createFrom2Points(points[tri[0]].point, points[tri[1]].point);
+ double scaleFac = 1 / rotVector301.getLength();
+ MultMat.setItems(new double[][]{
+ {scaleFac,0,0,0},
+ {0,scaleFac,0,0},
+ {0,0,scaleFac,0},
+ {0,0,0,1},
+ });
+ faceTo00.multiply(MultMat);
+ MultMat.setItems(new double[][]{
+ {1,0,0,-points[tri[0]].point.x},
+ {0,1,0,-points[tri[0]].point.y},
+ {0,0,1,-points[tri[0]].point.z},
+ {0,0,0,1},
+ });
+ faceTo00.multiply(MultMat);
+
+ }
+ // next, modify the faceto00 matrix to scale and shear such that point 3 matches up with UV point 3.
+ {
+ // first, apply the calculated matrices to their respective point 3s
+ Point3D pointFace = new Point3D();
+ pointFace.set(faceTo00.multiplyPoint3raw(points[tri[2]].point.x, points[tri[2]].point.y, points[tri[2]].point.z));
+
+ Point3D pointUV = new Point3D();
+ pointUV.set(uvTo00.multiplyPoint3raw(UVPoints[tri[2]].x, UVPoints[tri[2]].y, 0));
+
+ double xScale = pointUV.x / pointFace.x;
+ System.out.println("Scale: " + xScale);
+ MultMat.setItems(new double[][]{
+ {xScale, 0, 0, 0},
+ {0, 1, 0, 0},
+ {0, 0, 1, 0},
+ {0, 0, 0, 1},
+ });
+ faceTo00 = MultMat.multiplyGetResult(faceTo00);
+
+ double yShearFac = (pointUV.y - pointFace.y) / pointUV.x;
+ if (!Double.isFinite(yShearFac)){
+ yShearFac = 0;
+ }
+ MultMat.setItems(new double[][]{
+ {1, 0, 0, 0},
+ {yShearFac, 1, 0, 0},
+ {0, 0, 1, 0},
+ {0, 0, 0, 1},
+ });
+ faceTo00 = MultMat.multiplyGetResult(faceTo00);
+
+ // multiply final matrices and set their values in the class
+ perspectiveMappingMatrices[i][0] = uvTo00.getInverse().multiplyGetResult(faceTo00);
+ i += 1;
+ }
+ /*
+ System.out.println("UVPoints:###########################");
+ //DEBUG - printout the positions of old uv coords and new uv coords
+ Point3D newPoint = new Point3D();
+ for(int i = 0; i < 3; i+=1){
+ newPoint.set(uvTo00.multiplyPoint3raw(UVPoints[tri[i]].x, UVPoints[tri[i]].y, 0));
+ System.out.println(i + ": " + "xyzOld: " +
+ UVPoints[tri[i]].x + ", " + UVPoints[tri[i]].y + ", " + 0 + ", new:" +
+ newPoint.x + ", " + newPoint.y + ", " + newPoint.z);
+ }
+ System.out.println("FacePoints:###########################");
+ //DEBUG - printout the positions of old face coords and new face coords
+ Point3D newPoint = new Point3D();
+ for (int i = 0; i < 3; i += 1) {
+ newPoint.set(faceTo00.multiplyPoint3raw(points[tri[i]].point.x, points[tri[i]].point.y, points[tri[i]].point.z));
+ System.out.println(i + ": " + "xyzOld: " +
+ points[tri[i]].point.x + ", " + points[tri[i]].point.y + ", " + points[tri[i]].point.z + ", new:" +
+ newPoint.x + ", " + newPoint.y + ", " + newPoint.z);
+ }
+ */
+ }
+ // OPTIMIZATION - remove the z row from the matrix, since z output should always be zero.
+ // should decrease processing time of each pixel by a lot
+ }
+
+ private void bakePerspectiveMatrices(BufferedImage img, BufferedImage zBuf, BufferedImage debugImg, Matrix camMatrix, double FPDis, int scrX, int scrY){
+ // calculate matrix which converts 2d points into 3d points
+ {
+ // first, we get the rotated plane points and normal points
+ Point3D planePoint = points[0].getRotatedPoint();
+ Vector3D rotatedNormalVector = new Vector3D(0,0,0);
+ camMatrix.multiplyVec3to(normal, rotatedNormalVector);
+ /* next we define matrices according to the simultaneous equations:
+ 1, derived from projection:
+ point.x = scrX - (scrX*0.5*((fpdis*y)/(z) + 1));
+ becomes:
+ 0 = 0x + y(fpdis)+z(2*point.x/scrX - 1);
+ where point.x is the 2d x position of the point on the screen.
+
+ 2, derived from projection
+ point.y = (scrX*0.5*((fpdis*x)/(z) + (scrY/scrX)));
+ becomes:
+ 0 = x(scrX*fpDis) + 0y + z(scrY-2*point.y)
+ where point.y is the 2d y position of the point on the screen.
+
+ 3, derived from the distance to a plane.
+ a plane's normal vector dotted with a vector that goes from a
+ point that lies on a plane to the point to be tested,
+ equals the distance the tested point is from the plane.
+ This becomes:
+ x(normal.x)+y(normal.y)+z(normal.z) =
+ normal.x*planePoint.x + normal.y*planePoint.y + normal.z*planePoint.z
+ */
+ int x = 0;
+ int y = 0;
+ Matrix simulMat = new Matrix(3, 3);
+ simulMat.setItems(new double[][]{
+ {0, FPDis, (2*x/(double)scrX)-1},
+ {scrX*FPDis, 0, scrY-2*y},
+ {normal.x, normal.y, normal.z},
+ });
+ Matrix simulMat2 = new Matrix(1, 3);
+ simulMat2.setItems(new double[][]{
+ {0},
+ {0},
+ {normal.x * planePoint.x + normal.y * planePoint.y + normal.z * planePoint.z}
+ });
+ }
+ }
// 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
diff --git a/src/main/java/uk/org/floop/epq3d/Matrix.java b/src/main/java/uk/org/floop/epq3d/Matrix.java
index 5c980a9..fa9cfd9 100644
--- a/src/main/java/uk/org/floop/epq3d/Matrix.java
+++ b/src/main/java/uk/org/floop/epq3d/Matrix.java
@@ -66,6 +66,19 @@
result.z = point.x * getItem(0,2) + point.y* getItem(1,2) + point.z* getItem(2,2) + getItem(3,2);
} else {throw new RuntimeException("wrong-dimensions");}
}
+ public void multiplyVec3to(Vector3D vector, Vector3D result){
+ if(x==3){
+ double x = vector.x * getItem(0,0) + vector.y* getItem(1,0) + vector.z* getItem(2,0);
+ double y = vector.x * getItem(0,1) + vector.y* getItem(1,1) + vector.z* getItem(2,1);
+ double z = vector.x * getItem(0,2) + vector.y* getItem(1,2) + vector.z* getItem(2,2);
+ result.x = x; result.y = y; result.z = z;
+ } else if(x == 4){
+ double x = vector.x * getItem(0,0) + vector.y* getItem(1,0) + vector.z* getItem(2,0) + getItem(3,0);
+ double y = vector.x * getItem(0,1) + vector.y* getItem(1,1) + vector.z* getItem(2,1) + getItem(3,1);
+ double z = vector.x * getItem(0,2) + vector.y* getItem(1,2) + vector.z* getItem(2,2) + getItem(3,2);
+ result.x = x; result.y = y; result.z = z;
+ } else {throw new RuntimeException("wrong-dimensions");}
+ }
public double[] multiplyPoint3raw(double _x, double _y, double _z) {
double[] result = new double[3];
if(x ==3){
@@ -122,18 +135,18 @@
}
}
// transpose the adjoint matrix (unused because it's easier to do it while copying across
- /*{
- double temp;
- for(int mx = 0; mx < x; mx+=1){
- for(int my = 0; my <= mx; my += 1){
- if (my != mx){
- temp = adjoint.getItem(mx, my);
- adjoint.setItem(mx, my, adjoint.getItem(my, mx));
- adjoint.setItem(my, mx, temp);
- }
- }
- }
- }*/
+ //{
+ // double temp;
+ // for(int mx = 0; mx < x; mx+=1){
+ // for(int my = 0; my <= mx; my += 1){
+ // if (my != mx){
+ // temp = adjoint.getItem(mx, my);
+ // adjoint.setItem(mx, my, adjoint.getItem(my, mx));
+ // adjoint.setItem(my, mx, temp);
+ // }
+ // }
+ // }
+ // }
// copy the matrix to the result and divide by the determinant
for(int mx = 0; mx