package myGraphics.Perspective; import myGraphics.VectorGrafix.*; import java.awt.Graphics; import java.lang.Math; public class Camera { /* The following 3 variables determine the camera matrix */ protected Vector3d eyePoint = new Vector3d(); protected Vector3d aimPoint = new Vector3d(); protected double rollAngle = 0.0; /* * The following 2 variables provide information which can be used * to appropriately scale the viewport for viewport transformations */ public double imageWidth = 10.0; public double imageHeight = 10.0; protected Transformation3d inverseMatrix = new Transformation3d(); public Camera() { inverseMatrix = new Transformation3d(); eyePoint = new Vector3d(0.0, 0.0, 10.0); aimPoint = new Vector3d(0.0, 0.0, 0.0); rollAngle = 0.0; setupCamera(); imageHeight = 10.0; imageWidth = 10.0; } public Camera(Vector3d theEyepoint, Vector3d theAimpoint, double theRollAngle) { inverseMatrix = new Transformation3d(); eyePoint = theEyepoint.copy(); aimPoint = theAimpoint.copy(); rollAngle = theRollAngle; setupCamera(); imageWidth = 10.0; imageHeight = 10.0; } public Camera(Vector3d theEyepoint, Vector3d theAimpoint, double theRollAngle, double width, double height) { inverseMatrix = new Transformation3d(); eyePoint = theEyepoint.copy(); aimPoint = theAimpoint.copy(); rollAngle = theRollAngle; setupCamera(); imageWidth = width; imageHeight = height; } public void setEye(Vector3d e) { eyePoint = e.copy(); setupCamera(); } public void setAim(Vector3d a) { aimPoint = a.copy(); setupCamera(); } protected void setupCamera(){ Vector3d Cx = new Vector3d(); // Camera x vector Vector3d Cy = new Vector3d(); // Camera y vector Vector3d Cz = new Vector3d(); // Camera z vector Vector3d temp = new Vector3d(); Vector3d Gy = new Vector3d(0.0, 1.0, 0.0, 0.0); // global y-axis vector Vector3d Ce = new Vector3d(eyePoint.x(), eyePoint.y(), eyePoint.z(), 0.0); // eye as vector /* * Important: The following code needs modification. There are conditions * where the methods below will yield vectors of zero length, resulting * in divide by zero errors. The code should really account for these * conditions: (eyepoint == aimPoint) or (eye vector parallel to Gy) */ Cz = eyePoint.minus(aimPoint).normalize(); Cx = Gy.cross_product(Cz).normalize(); Cy = Cz.cross_product(Cx).normalize(); for (int i = 0; i < 3; i++) { inverseMatrix.set_term(0, i, Cx.get_term(i)); inverseMatrix.set_term(1, i, Cy.get_term(i)); inverseMatrix.set_term(2, i, Cz.get_term(i)); inverseMatrix.set_term(3, i, 0.0); } inverseMatrix.set_term(0, 3, Cx.negate().dot_product(Ce)); inverseMatrix.set_term(1, 3, Cy.negate().dot_product(Ce)); inverseMatrix.set_term(2, 3, Cz.negate().dot_product(Ce)); inverseMatrix.set_term(3, 0, 0.0); inverseMatrix.set_term(3, 1, 0.0); inverseMatrix.set_term(3, 2, 0.0); inverseMatrix.set_term(3, 3, 1.0); if (rollAngle != 0.0) { inverseMatrix = inverseMatrix.z_rotate(-rollAngle); } } public double focalLength() { return eyePoint.distanceTo(aimPoint); } public void rotate(double x_angle, double y_angle, double z_angle) { /* * To rotate the camera on its own x and y axes, * we "anti-rotate" the world in camera space... * * The aimPoint lives in world space, and must be "anti-rotated" * around the eyePoint, according to the camera's coordinate system. * This means it must first be transformed to camera space, rotated, * and transformed back using the OLD forward camera matrix. */ /* Prepare the "anti-rotation" matrix */ Transformation3d rotationMatrix = new Transformation3d(); if (x_angle != 0.0) { rotationMatrix = rotationMatrix.x_rotate(-x_angle); } if (y_angle != 0.0) { rotationMatrix = rotationMatrix.y_rotate(-y_angle); } if (z_angle != 0.0) { rotationMatrix = rotationMatrix.z_rotate(-z_angle); rollAngle += z_angle; } /* Create the current forward matrix (before rotating camera) */ Transformation3d oldForwardMatrix = new Transformation3d(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { oldForwardMatrix.set_term(i, j, inverseMatrix.get_term(j, i)); } oldForwardMatrix.set_term(i, 3, eyePoint.get_term(i)); } /* Rotate the camera by anti-rotating the world in camera space */ inverseMatrix = inverseMatrix.appliedBefore(rotationMatrix); /* Bring the aimPoint into camera spaceand anti-rotate it */ aimPoint = inverseMatrix.apply(aimPoint); /* Return the aimPoint to world space */ aimPoint = oldForwardMatrix.apply(aimPoint); } public void translate(double Tx, double Ty, double Tz) { /* * Move the camera's position in camera space, preserving its direction * vector and focal length. To accomplish this, we move the world * by the opposite vector in camera space. */ inverseMatrix = inverseMatrix.translate(-Tx, -Ty, -Tz); /* * The eyepoint and the aimpoint move in world space, but according * to the vectors which define the camera coordinate system, not * the world coordinate system. */ Vector3d Cx = new Vector3d(); // Camera x vector Vector3d Cy = new Vector3d(); // Camera y vector Vector3d Cz = new Vector3d(); // Camera z vector for (int i = 0; i < 3; i++) { Cx.set_term(i, inverseMatrix.get_term(0, i)); Cy.set_term(i, inverseMatrix.get_term(1, i)); Cz.set_term(i, inverseMatrix.get_term(2, i)); } Cx.set_term(3, 0.0); Cy.set_term(3, 0.0); Cz.set_term(3, 0.0); Transformation3d trans = new Transformation3d(); trans = trans.translate(Cx.times(Tx)); trans = trans.translate(Cy.times(Ty)); trans = trans.translate(Cz.times(Tz)); eyePoint = trans.apply(eyePoint); aimPoint = trans.apply(aimPoint); } public void translate(Vector3d v) { translate(v.x(), v.y(), v.z()); } public Vector2d projectPoint(Vector3d thePoint) { /* * This function takes as its argument a point ALREADY * TRANSFORMED INTO CAMERA-SPACE, and projects it onto the * 2D image-plane in 3D perspective. */ return new Vector2d(thePoint.x() * -focalLength() / thePoint.z(), thePoint.y() * -focalLength() / thePoint.z()); } public Vector3d toCameraSpace(Vector3d originalPoint) { /* * Transform the point from world-space to camera space. */ return inverseMatrix.apply(originalPoint); } public boolean canSee(Vector3d apparentPoint) { /* * This is a simplistic visibility algorithm. If it's in front * of the camera, we can see it. Not that the point is expected to * be already transformed into camera-space. */ return (apparentPoint.z() < 0); } public void displayLine(Graphics g, Viewport theViewport, Vector3d from, Vector3d to) { /* * Displays, in 3D perspective, a line between two points * given in world-space. */ Vector3d apparent1 = toCameraSpace(from); Vector3d apparent2 = toCameraSpace(to); if (canSee(apparent1) && canSee(apparent2)) { Vector2d point1 = projectPoint(apparent1); Vector2d point2 = projectPoint(apparent2); point1 = theViewport.window2viewport(point1); point2 = theViewport.window2viewport(point2); g.drawLine((int) point1.x(), (int) point1.y(), (int) point2.x(), (int) point2.y()); } } }