/* Copyright (C) 2001, 2007 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.view; import gov.nasa.worldwind.geom.*; import gov.nasa.worldwind.globes.Globe; import gov.nasa.worldwind.util.Logging; /** * @author dcollins * @version $Id: BasicOrbitViewModel.java 4810 2008-03-26 00:50:55Z dcollins $ */ public class BasicOrbitViewModel implements OrbitViewModel { private static class BasicModelCoordinates implements OrbitViewModel.ModelCoordinates { private final Position center; private final Angle heading; private final Angle pitch; private final double zoom; private BasicModelCoordinates(Position center, Angle heading, Angle pitch, double zoom) { if (center == null) { String message = Logging.getMessage("nullValue.PositionIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (heading == null || pitch == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } this.center = center; this.heading = heading; this.pitch = pitch; this.zoom = zoom; } public Position getCenterPosition() { return this.center; } public Angle getHeading() { return this.heading; } public Angle getPitch() { return this.pitch; } public double getZoom() { return this.zoom; } } public BasicOrbitViewModel() { } public Matrix computeTransformMatrix(Globe globe, Position center, Angle heading, Angle pitch, double zoom) { if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (center == null) { String message = Logging.getMessage("nullValue.PositionIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (heading == null || pitch == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Construct the model-view transform matrix for the specified coordinates. // Because this is a model-view transform, matrices are applied in reverse order. Matrix transform = Matrix.IDENTITY; // Zoom. transform = transform.multiply(Matrix.fromTranslation(0, 0, -zoom)); // Heading and pitch. transform = transform.multiply(Matrix.fromRotationX(pitch.multiply(-1))); transform = transform.multiply(Matrix.fromRotationZ(heading)); // Center position. transform = transform.multiply(computeCenterTransform(globe, center)); return transform; } public ModelCoordinates computeModelCoordinates(Globe globe, Vec4 eyePoint, Vec4 centerPoint, Vec4 up) { if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (eyePoint == null || centerPoint == null || up == null) { String message = Logging.getMessage("nullValue.Vec4IsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } Matrix modelview = Matrix.fromLookAt(eyePoint, centerPoint, up); return computeModelCoordinates(globe, modelview, centerPoint); } public ModelCoordinates computeModelCoordinates(Globe globe, Matrix modelTransform, Vec4 centerPoint) { if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (modelTransform == null) { String message = Logging.getMessage("nullValue.MatrixIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (centerPoint == null) { String message = Logging.getMessage("nullValue.Vec4IsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Compute the center position, and center position transform. Position centerPos = globe.computePositionFromPoint(centerPoint); Matrix centerTransform = computeCenterTransform(globe, centerPos); Matrix centerTransformInv = centerTransform.getInverse(); if (centerTransformInv == null) { String message = Logging.getMessage("generic.NoninvertibleMatrix"); Logging.logger().severe(message); throw new IllegalStateException(message); } // Compute the heading-pitch-zoom transform. Matrix hpzTransform = modelTransform.multiply(centerTransformInv); // Extract the heading, pitch, and zoom values from the transform. Angle heading = hpzTransform.getRotationZ(); Angle pitch = hpzTransform.getRotationX(); Vec4 zoomVec = hpzTransform.getTranslation(); if (heading != null && pitch != null && zoomVec != null) return new BasicModelCoordinates(centerPos, heading, pitch.multiply(-1), zoomVec.getLength3()); else return null; } private static Matrix computeCenterTransform(Globe globe, Position center) { Matrix transform = Matrix.IDENTITY; if (globe != null && center != null) { // The view eye position will be the same as the center position. // This is only the case without any zoom, heading, and pitch. Vec4 eyePoint = globe.computePointFromPosition(center); // The view forward direction will be colinear with the // geoid surface normal at the center position. Vec4 normal = globe.computeSurfaceNormalAtPoint(eyePoint); Vec4 lookAtPoint = eyePoint.subtract3(normal); // The up vector computed here can be approximate, because // fromLookAt() will create a correct up from a general direction. // The up vector, however cannot be zero and cannot be colinear with the // forward vector (forward=lookAtPoint-eyePoint). Vec4 up = approximateUpVector(globe, center); // Creates a viewing matrix looking from eyePoint towards lookAtPoint, // with the given up direction. The forward, right, and up vectors // contained in the matrix are guaranteed to be orthogonal. This means // that the Matrix's up may not be equivalent to the specified up vector // here (though it will point in the same general direction). // In this case, the forward direction would not be affected. transform = Matrix.fromLookAt(eyePoint, lookAtPoint, up); } return transform; } private static Vec4 approximateUpVector(Globe globe, Position center) { Matrix transform = Matrix.IDENTITY; if (globe != null && center != null) { Angle lat = center.getLatitude(); Angle lon = center.getLongitude(); // Center lat/lon is expressed as 3D rotation, which uses "Geocentric" latitude. In order for the // center to appear at the specified latitude, we must convert the incoming coordinates from // "Geodetic" coordinates to "Geocentric" coordinates. Angle latGeodetic = geodeticToGeocentric(globe, lat, center.getElevation()); transform = transform.multiply(Matrix.fromRotationX(latGeodetic)); transform = transform.multiply(Matrix.fromRotationY(lon.multiply(-1))); } return Vec4.UNIT_Y.transformBy4(transform.getInverse()); } private static Angle geodeticToGeocentric(Globe globe, Angle latitude, double elevation) { if (globe != null && latitude != null) { Vec4 point = globe.computePointFromPosition(latitude, Angle.ZERO, elevation); PolarPoint polarPoint = PolarPoint.fromCartesian(point); return polarPoint.getLatitude(); } else { return latitude; } } }