public void reset(int[] rotation, int cx, int cy, int cz) { this.projected.removeAllElements(); this.rotation = rotation; this.rotationMatrix = new int[3][3]; MatrixMath.getRotationMatrix(this.rotationMatrix, rotation); this.cx = cx; this.cy = cy; this.cz = cz; }
public void project(Body body) { Shape shape = body.shape; int dx = body.center[0] - cx; int dy = body.center[1] - cy; int dz = body.center[2] - cz; int[] dpoints = new int[] {dx, dy, dz}; int[] rotateddpoints = MatrixMath.multiplyVector(dpoints, dpoints, this.rotationMatrix); int rdxf = rotateddpoints[0]; int rdyf = rotateddpoints[1]; int rdzf = rotateddpoints[2]; int rdx = FixedMath.getInteger(rdxf); int rdy = FixedMath.getInteger(rdyf); int rdz = FixedMath.getInteger(rdzf); int radius = FixedMath.getInteger(shape.getMaxRadius()); // TODO : rotate the body center around the viewers center by the view angle boolean mightBeVisible; // check that the body is actually visible at all (not sure about deye check) if ((rdz - radius) < -this.deye && rdz < 0) { // double pow = Math.pow(2, // Math.abs((double)FixedMath.getInteger(dz)/(double)this.shrinkage)); int adjustedRadius = (radius * deye) / -rdz; // check that the mesh isn't so small that it won't be visible or should // be simplified to a point if (adjustedRadius >= this.minRadius) { int screenycenter = (rdy * deye) / -rdz; if ((screenycenter - adjustedRadius < this.height / 2) && (screenycenter + adjustedRadius > -this.height / 2)) { int screenxcenter = (rdx * deye) / -rdz; mightBeVisible = ((screenxcenter - adjustedRadius < this.width / 2) && (screenxcenter + adjustedRadius > -this.width / 2)); // if(!mightBeVisible) // { // System.out.println("outside x bounds of screen"); // } } else { mightBeVisible = false; // System.out.println("outside y bounds of screen "+rdy+" "+screenycenter); } } else { // TODO : simplify to a point if it's reasonably close // too far away mightBeVisible = false; // System.out.println("too small "+body.center[2]+" "+rdz+" "+radius+" -> "+adjustedRadius); } } else { mightBeVisible = false; // System.out.println("behind pane "+rdz); } // mightBeVisible = (rdz - radius) <= 0; if (mightBeVisible) { if (shape instanceof Mesh) { Mesh mesh = (Mesh) shape; int[][] working = body.rotatedMatrix; if (working == null) { int[][] points = mesh.baseMatrix; working = new int[points.length][points[0].length]; body.rotatedMatrix = working; } addMesh(mesh, body, working, rdxf, rdyf, rdzf); } else if (shape instanceof Composite) { Composite composite = (Composite) shape; addComposite(composite, body, rdxf, rdyf, rdzf); } else if (shape instanceof Sphere) { // TODO : sphere } else { throw new IllegalArgumentException("unrecognised shape type : " + shape.getClass()); } } }
private void addMesh(Mesh mesh, Body body, int[][] working, int rdxf, int rdyf, int rdzf) { int[][] points = mesh.baseMatrix; // MatrixMath.translate(points, working, dx, dy, dz); // project onto the view plane // int[][] target = new int[mesh.baseMatrix.length][mesh.baseMatrix[0].length]; int[][] target = working; int[] rotation = new int[5]; int[][] rotationProjection = new int[3][3]; if (mesh instanceof AnimatedMesh) { // we need to redo the rotation AnimatedMesh animatedMesh = (AnimatedMesh) mesh; // MatrixMath.multiplyQuaternion(rotation, body.rotation, // animatedMesh.animation.getRotation()); // MatrixMath.multiplyQuaternion(rotation, rotation, this.rotation); MatrixMath.multiplyQuaternion(rotation, animatedMesh.animation.getRotation(), body.rotation); MatrixMath.multiplyQuaternion(rotation, rotation, this.rotation); MatrixMath.getRotationMatrix(rotationProjection, rotation); } else { MatrixMath.multiplyQuaternion(rotation, body.rotation, this.rotation); MatrixMath.getRotationMatrix(rotationProjection, rotation); } MatrixMath.multiply(target, points, rotationProjection); // MatrixMath.rotate(target, points, new int[4][4], rx, ry, rz); points = target; for (int row = points.length; row > 0; ) { row--; points[row][0] += rdxf; points[row][1] += rdyf; points[row][2] += rdzf; } // TODO : replace with single multiplied out matrix from above!! for (int row = points.length; row > 0; ) { row--; // points[row][0] += rdx; // points[row][1] += rdy; // points[row][2] += rdz; for (int col = 3; col > 0; ) { col--; // points[row][col] += points[points.length-1][col]; // convert back to pixel values, after all, that's all the precision we'll need from now on // points[row][col] += points[points.length-1][col]; points[row][col] = FixedMath.getInteger(points[row][col]); } // perspective // TODO : have a cut off point for when we even stop representing dots int z = points[row][2]; if (z >= 0) { z = -1; } points[row][0] = (points[row][0] * deye) / -z; points[row][1] = (points[row][1] * deye) / -z; } ProjectionMesh projectionMesh = new ProjectionMesh(points); byte[][] faces = mesh.faces; projectionMesh.color = mesh.color; // work out the mesh's convex hull for intersections and rendering beauty // TODO : reuse working somehow byte[] stack = new byte[points.length]; int stackLength = getConvexHull(points, new byte[points.length], stack); projectionMesh.hull = stack; projectionMesh.hullLength = stackLength; // add the convex hull to the lines // to reduce intersection detection and allow accurate // outlining for (int i = stackLength; i > 0; ) { i--; byte fromIndex = stack[i]; byte nextIndex = stack[(i + 1) % stackLength]; int[] from = points[fromIndex]; int[] next = points[nextIndex]; ProjectionLine line = new ProjectionLine(from, next, fromIndex, nextIndex, true); projectionMesh.lines.addElement(line); } for (int i = faces.length; i > 0; ) { i--; byte[] faceIndices = faces[i]; // beckface removal int minFaceIndex = faceIndices.length - 1; int minIndex = faceIndices[minFaceIndex]; int miny = points[minIndex][1]; int minx = points[minIndex][0]; for (int j = faceIndices.length - 1; j > 0; ) { j--; int index = (int) faceIndices[j]; if (points[index][1] < miny || (points[index][1] == miny && points[index][0] < minx)) { minFaceIndex = j; miny = points[index][1]; minx = points[index][0]; } } int preIndex = minFaceIndex - 1; if (preIndex < 0) { preIndex = faceIndices.length - 1; } preIndex = faceIndices[preIndex]; int postIndex = faceIndices[(minFaceIndex + 1) % faceIndices.length]; int prex = points[preIndex][0]; int prey = points[preIndex][1]; int postx = points[postIndex][0]; int posty = points[postIndex][1]; int predx = prex - minx; int predy = prey - miny; int postdx = postx - minx; int postdy = posty - miny; boolean faceOK; if (predx == 0 && postdx == 0) { // it's flat, don't draw it // System.out.println("no dx"); faceOK = false; } else if (predx == 0) { // the incoming line comes from the right // System.out.println("no predx "+preIndex+"("+prex+","+prey+") // "+minIndex+"("+minx+","+miny+") "+postIndex+"("+postx+","+posty+")"); faceOK = postdx > 0; } else if (postdx == 0) { // the outbound line goes to the right // System.out.println("no postdx "+preIndex+"("+prex+","+prey+") // "+minIndex+"("+minx+","+miny+") "+postIndex+"("+postx+","+posty+")"); faceOK = predx < 0; } else { // multiply it out a bit, we're losing precision, preferably this multiplier will be the // width // of the screen since that will ensure that it's longer than almost any value of dx int prem = (predy * 1024) / predx; int postm = (postdy * 1024) / postdx; // System.out.println("not vertical "+preIndex+"("+prex+","+prey+") // "+minIndex+"("+minx+","+miny+") "+postIndex+"("+postx+","+posty+")"); if (prem == postm) { // it's flat faceOK = false; } else if (prem < 0 && postm < 0 || prem >= 0 && postm >= 0) { faceOK = prem > postm; } else { faceOK = prem < 0 && postm >= 0; } // or, more accurately (negatives dys and zeros are impossible in this context) // faceOK = predy * postdx > predx * postdy; // double prem = // (double)FixedMath.getInteger(predy)/(double)FixedMath.getInteger(predx); // double postm = // (double)FixedMath.getInteger(postdy)/(double)FixedMath.getInteger(postdx); // double preAngle = Math.atan(prem); // double postAngle = Math.atan(postm); // System.out.println(preAngle); // System.out.println(postAngle); // faceOK = preAngle < postAngle; } if (faceOK) { for (int j = faceIndices.length; j > 0; ) { j--; byte index = faceIndices[j]; byte nextIndex = faceIndices[(j + 1) % faceIndices.length]; // if a line with the same indices exists then we don't need to // add it again boolean found = false; for (int k = projectionMesh.lines.size(); k > 0; ) { k--; ProjectionLine line = (ProjectionLine) projectionMesh.lines.elementAt(k); if (line.fromIndex == index && line.toIndex == nextIndex || line.toIndex == index && line.fromIndex == nextIndex) { found = true; break; } else if (line.perimeter) { // the line's as long as it can be, hence may contain other lines if (line.fromIndex == index) { int[] common = points[index]; int[] linePoint = points[line.toIndex]; int[] testPoint = points[nextIndex]; if (this.isLeft(common, linePoint, testPoint) == 0) { found = true; break; } } else if (line.toIndex == index) { int[] common = points[index]; int[] linePoint = points[line.fromIndex]; int[] testPoint = points[nextIndex]; if (this.isLeft(common, linePoint, testPoint) == 0) { found = true; break; } } else if (line.fromIndex == nextIndex) { int[] common = points[nextIndex]; int[] linePoint = points[line.toIndex]; int[] testPoint = points[index]; if (this.isLeft(common, linePoint, testPoint) == 0) { found = true; break; } } else if (line.toIndex == nextIndex) { int[] common = points[nextIndex]; int[] linePoint = points[line.fromIndex]; int[] testPoint = points[index]; if (this.isLeft(common, linePoint, testPoint) == 0) { found = true; break; } } else { // TODO : implement this... // test to see if the line sits within the other although this // is probably a very unusual case found = false; } } } if (!found) { ProjectionLine line = new ProjectionLine(projectionMesh, index, nextIndex, false); projectionMesh.lines.addElement(line); } } } else { // System.out.println("culled "+i); } } this.projected.addElement(projectionMesh); }