/* 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; import gov.nasa.worldwind.render.DrawContext; import javax.media.opengl.glu.GLU; import javax.media.opengl.GL; /** * @author dcollins * @version $Id: ViewSupport.java 4927 2008-04-04 21:27:45Z dcollins $ */ public class ViewSupport { private final GLU glu = new GLU(); public ViewSupport() { } public void loadGLViewState(DrawContext dc, Matrix modelview, Matrix projection) { if (dc == null) { String message = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (dc.getGL() == null) { String message = Logging.getMessage("nullValue.DrawingContextGLIsNull"); Logging.logger().severe(message); throw new IllegalStateException(message); } if (modelview == null) { Logging.logger().fine("nullValue.ModelViewIsNull"); } if (projection == null) { Logging.logger().fine("nullValue.ProjectionIsNull"); } double[] matrixArray = new double[16]; GL gl = dc.getGL(); // Store the current matrix-mode state. int[] matrixMode = new int[1]; gl.glGetIntegerv(GL.GL_MATRIX_MODE, matrixMode, 0); // Apply the model-view matrix to the current OpenGL context. gl.glMatrixMode(GL.GL_MODELVIEW); if (modelview != null) { modelview.toArray(matrixArray, 0, false); gl.glLoadMatrixd(matrixArray, 0); } else { gl.glLoadIdentity(); } // Apply the projection matrix to the current OpenGL context. gl.glMatrixMode(GL.GL_PROJECTION); if (projection != null) { projection.toArray(matrixArray, 0, false); gl.glLoadMatrixd(matrixArray, 0); } else { gl.glLoadIdentity(); } // Restore matrix-mode state. gl.glMatrixMode(matrixMode[0]); } public Vec4 project(Vec4 point, Matrix modelview, Matrix projection, java.awt.Rectangle viewport) { if (point == null) { String message = Logging.getMessage("nullValue.PointIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (modelview == null || projection == null) { String message = Logging.getMessage("nullValue.MatrixIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (viewport == null) { String message = Logging.getMessage("nullValue.RectangleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // GLU expects matrices as column-major arrays. double[] modelviewArray = new double[16]; double[] projectionArray = new double[16]; modelview.toArray(modelviewArray, 0, false); projection.toArray(projectionArray, 0, false); // GLU expects the viewport as a four-component array. int[] viewportArray = new int[] {viewport.x, viewport.y, viewport.width, viewport.height}; double[] result = new double[3]; if (!this.glu.gluProject( point.x, point.y, point.z, modelviewArray, 0, projectionArray, 0, viewportArray, 0, result, 0)) { return null; } return Vec4.fromArray3(result, 0); } public Vec4 unProject(Vec4 windowPoint, Matrix modelview, Matrix projection, java.awt.Rectangle viewport) { if (windowPoint == null) { String message = Logging.getMessage("nullValue.PointIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (modelview == null || projection == null) { String message = Logging.getMessage("nullValue.MatrixIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (viewport == null) { String message = Logging.getMessage("nullValue.RectangleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // GLU expects matrices as column-major arrays. double[] modelviewArray = new double[16]; double[] projectionArray = new double[16]; modelview.toArray(modelviewArray, 0, false); projection.toArray(projectionArray, 0, false); // GLU expects the viewport as a four-component array. int[] viewportArray = new int[] {viewport.x, viewport.y, viewport.width, viewport.height}; double[] result = new double[3]; if (!this.glu.gluUnProject( windowPoint.x, windowPoint.y, windowPoint.z, modelviewArray, 0, projectionArray, 0, viewportArray, 0, result, 0)) { return null; } return Vec4.fromArray3(result, 0); } public Line computeRayFromScreenPoint(double x, double y, Matrix modelview, Matrix projection, java.awt.Rectangle viewport) { if (modelview == null || projection == null) { String message = Logging.getMessage("nullValue.MatrixIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (viewport == null) { String message = Logging.getMessage("nullValue.RectangleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Compute a ray originating from the view, and passing through the screen point (x, y). // // Taken from the "OpenGL Technical FAQ & Troubleshooting Guide", // section 20.010 "How can I know which primitive a user has selected with the mouse?" // // http://www.opengl.org/resources/faq/technical/selection.htm#sele0010 Matrix modelViewInv = modelview.getInverse(); if (modelViewInv == null) return null; Vec4 eye = Vec4.UNIT_W.transformBy4(modelViewInv); if (eye == null) return null; double yInv = viewport.height - y - 1; Vec4 a = this.unProject(new Vec4(x, yInv, 0, 0), modelview, projection, viewport); Vec4 b = this.unProject(new Vec4(x, yInv, 1, 0), modelview, projection, viewport); if (a == null || b == null) return null; return new Line(eye, b.subtract3(a).normalize3()); } public double computePixelSizeAtDistance(double distance, Angle fieldOfView, java.awt.Rectangle viewport) { if (fieldOfView == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (viewport == null) { String message = Logging.getMessage("nullValue.RectangleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Compute the coefficient for computing the size of a pixel. double pixelSizeScale; if (viewport.getWidth() > 0) pixelSizeScale = 2 * fieldOfView.tanHalfAngle() / viewport.getWidth(); else pixelSizeScale = 0; return Math.abs(distance) * pixelSizeScale; } public double computeHorizonDistance(Globe globe, double elevation) { if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (elevation <= 0) return 0; double radius = globe.getMaximumRadius(); return Math.sqrt(elevation * (2 * radius + elevation)); } public double computeElevationAboveSurface(DrawContext dc, Position position) { if (dc == null) { String message = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } Globe globe = dc.getGlobe(); if (globe == null) { String message = Logging.getMessage("nullValue.DrawingContextGlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (position == null) { String message = Logging.getMessage("nullValue.Vec4IsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } Position surfacePosition = null; // Look for the surface geometry point at 'position'. Vec4 pointOnGlobe = dc.getPointOnGlobe(position.getLatitude(), position.getLongitude()); if (pointOnGlobe != null) surfacePosition = globe.computePositionFromPoint(pointOnGlobe); // Fallback to using globe elevation values. if (surfacePosition == null) surfacePosition = new Position( position.getLatLon(), globe.getElevation(position.getLatitude(), position.getLongitude()) * dc.getVerticalExaggeration()); return position.getElevation() - surfacePosition.getElevation(); } }