import robotrace.Vector; import static java.lang.Math.*; import robotrace.GlobalState; /** * Implementation of a camera with a position and orientation. */ class Camera { /** The position of the camera. */ public Vector eye = new Vector(3f, 6f, 5f); /** The point to which the camera is looking. */ public Vector center = Vector.O; /** The up vector. */ public Vector up = Vector.Z; /** Race track used. */ private RaceTrack track; /** * A reference to the global game state from RobotRace. */ private final GlobalState gs; /** * Robots that are to be tracked by the camera. */ private final Robot[] robots; public Camera(GlobalState gs, RaceTrack track, Robot[] robots) { this.gs = gs; this.track = track; this.robots = robots; } /** * Updates the camera viewpoint and direction based on the * selected camera mode. */ public void update(int mode) { if (1 == mode) { // Helicopter mode setHelicopterMode(); } else if (2 == mode) { // Motor cycle mode setMotorCycleMode(); } else if (3 == mode) { // First person mode setFirstPersonMode(); } else if (4 == mode) { // Auto mode setAutoMode(); } else { // Default mode setDefaultMode(); } } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the camera's default mode. */ private void setDefaultMode() { /* z | * | vDist % * | % * Ez * |%________*________ y * Ex / % * * / s % * * x / - - - - - - - * * Ey * phi is angle between vDist and XY plane (Z direction) * theta is angle between X-axis and s (XY plane) * E = (Ex, Ey, Ez) * sin phi = Ez / vDist => Ez = vDist * sin phi * cos phi = s / vDist => s = vDist * cos phi * Ex = s * sin theta * Ey = s * cos theta */ float Ex, Ey, Ez, s; Ez = gs.vDist * (float) sin(gs.phi); s = gs.vDist * (float) cos(gs.phi); Ex = s * (float) sin(gs.theta); Ey = s * (float) cos(gs.theta); eye = new Vector(Ex, Ey, Ez); // WASD action: center point and eye point translate double Cx, Cy, Cz; // x and y are swapped because robot looks in y direction Cx = -gs.cnt.y(); Cy = gs.cnt.x(); Cz = gs.cnt.z(); center = new Vector(Cx, Cy, Cz); eye = eye.add(center); // just look straight forward up = Vector.Z; } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the helicopter mode. */ private void setHelicopterMode() { /** * In the Helicopter view, the camera (eye point) is located above the * robots. */ Robot focus = getFocusedRobot(); // center at the chosen robot. center = track.getPointForLane(focus.getTimePos(), focus.getLane()); /* look in the direction where the robots walks, namely the tangent Add the actual robot position to the tangent vector, and calculate the normal vector based on the resulting vector. This is the up vector. */ Vector robotPos = track.getPointForLane(focus.getTimePos(), focus.getLane()); Vector robotTangent = track.getTangent(focus.getTimePos()); Vector totalVector = robotTangent.add(robotPos); up = new Vector(-totalVector.y(), totalVector.x(), 0); // "above" is 10 meters. eye = center.add(new Vector(0, 0, 10f)); } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the motorcycle mode. */ private void setMotorCycleMode() { /** * In the Motor Cycle view, the camera is at the side of a track, * following the robots. */ Robot focus = getFocusedRobot(); // Center at the focused robot. center = track.getPointForLane(focus.getTimePos(), focus.getLane()); // We are looking at the robot from the side. up = Vector.Z; // look at a distance of 10 meters from the center of the first lane eye = track.getPointForLane(focus.getTimePos(), 10); // assume that the motor camera is one meter above the race track. eye = eye.add(new Vector(0, 0, 1f)); } /** * Computes {@code eye}, {@code center}, and {@code up}, based * on the first person mode. */ private void setFirstPersonMode() { /** * First person mode: look from the slowest robot forward. */ Robot focus = getSlowestRobot(); // trivial: looks from the robot POV. eye = track.getPointForLane(focus.getTimePos(), focus.getLane()); // robots are two meter, look from head. eye = eye.add(new Vector(0, 0, 2f)); // The question is similar to question 2b of the intermediate test // http://www.win.tue.nl/~vanwijk/2IV60/2IV60_test_exam_161213_answers.pdf // C(t) = E(t) + P'(t) (P'(t) is the tangent vector). Vector robotTangent = track.getTangent(focus.getTimePos()); // look at a point one meter in front of the robot // TODO: this gets badly messed up when perspective is modified center = eye.add(robotTangent); // trivial: look forward, so up vector points up. up = Vector.Z; } /** * Alternates between the available camera modes. */ private void setAutoMode() { double slowest_pos = robots[0].getTimePos(); double fastest_pos = robots[0].getTimePos(); for (Robot robot : robots) { slowest_pos = Math.min(slowest_pos, robot.getTimePos()); fastest_pos = Math.max(fastest_pos, robot.getTimePos()); } double distance = Robot.racepost2meter(fastest_pos - slowest_pos); System.err.println("dist+" + distance); // the helicopter view is more suitable if robots are more distant if (distance > 5) { setHelicopterMode(); } else { setMotorCycleMode(); } } /** * Returns the robot on which the camera is focused. */ private Robot getFocusedRobot() { Robot selected = robots[0]; for (Robot robot : robots) { // Many possibilities here, fastest, slowest, loser, winner... if (selected.getSpeed() < robot.getSpeed()) { // select the fastest accelerating robot. selected = robot; } } return selected; } /** * Returns the robots which has the lowest Global State Time position. */ private Robot getSlowestRobot() { Robot slowest = robots[0]; for (Robot robot : robots) { if (robot.getTimePos() < slowest.getTimePos()) { slowest = robot; } } return slowest; } }