Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / AbstractView.java
blob7de4ce0d17f98ae11f39695d30178790707b4e2f
1 /*
2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
4 All Rights Reserved.
5 */
6 package gov.nasa.worldwind;
8 import gov.nasa.worldwind.geom.*;
9 import gov.nasa.worldwind.geom.Point;
11 import javax.media.opengl.*;
12 import javax.media.opengl.glu.*;
13 import java.awt.*;
14 import java.util.logging.Level;
16 /**
17 * @author Paul Collins
18 * @version $Id: AbstractView.java 1794 2007-05-08 22:07:21Z dcollins $
20 public abstract class AbstractView extends WWObjectImpl implements View
22 private static final Double DefaultFov;
24 static
26 DefaultFov = Configuration.getDoubleValue(AVKey.FOV, 45d);
29 // Current OpenGL viewing state.
30 private Matrix4 modelView;
31 private Matrix4 projection;
32 private Rectangle viewport;
33 private Angle fieldOfView = Angle.fromDegrees(DefaultFov);
34 // Current DrawContext state.
35 private Globe globe = null;
36 private double verticalExaggeration = -1;
37 // Cached viewing attribute computations.
38 private Point eye = null;
39 private Point up = null;
40 private Point forward = null;
41 private Frustum frustumInModelCoords = null;
42 private double pixelSizeScale = -1;
43 private double horizonDistance = -1;
44 // Temporary state.
45 private static final int[] matrixMode = new int[1];
46 private static final int[] viewportArray = new int[4];
48 public void apply(DrawContext dc)
50 validateDrawContext(dc);
51 this.globe = dc.getGlobe();
52 this.verticalExaggeration = dc.getVerticalExaggeration();
54 // Get the current OpenGL viewport state.
55 dc.getGL().glGetIntegerv(GL.GL_VIEWPORT, viewportArray, 0);
56 this.viewport = new Rectangle(viewportArray[0], viewportArray[1], viewportArray[2], viewportArray[3]);
58 this.clearCachedAttributes();
59 this.doApply(dc);
62 protected abstract void doApply(DrawContext dc);
64 private void clearCachedAttributes()
66 this.eye = null;
67 this.up = null;
68 this.forward = null;
69 this.frustumInModelCoords = null;
70 this.pixelSizeScale = -1;
71 this.horizonDistance = -1;
74 protected void applyMatrixState(DrawContext dc, Matrix4 modelView, Matrix4 projection)
76 validateDrawContext(dc);
77 if (modelView == null)
79 String message = WorldWind.retrieveErrMsg("AbstractView.ModelViewIsNull");
80 WorldWind.logger().log(Level.FINE, message);
82 if (projection == null)
84 String message = WorldWind.retrieveErrMsg("AbstractView.ProjectionIsNull");
85 WorldWind.logger().log(Level.FINE, message);
88 GL gl = dc.getGL();
90 // Store the current matrix-mode state.
91 gl.glGetIntegerv(GL.GL_MATRIX_MODE, matrixMode, 0);
92 int newMatrixMode = matrixMode[0];
94 // Apply the model-view matrix to the current OpenGL context held by 'dc'.
95 if (newMatrixMode != GL.GL_MODELVIEW)
97 newMatrixMode = GL.GL_MODELVIEW;
98 gl.glMatrixMode(newMatrixMode);
100 if (modelView != null)
101 gl.glLoadMatrixd(modelView.getEntries(), 0);
102 else
103 gl.glLoadIdentity();
105 // Apply the projection matrix to the current OpenGL context held by 'dc'.
106 newMatrixMode = GL.GL_PROJECTION;
107 gl.glMatrixMode(newMatrixMode);
108 if (projection != null)
109 gl.glLoadMatrixd(projection.getEntries(), 0);
110 else
111 gl.glLoadIdentity();
113 // Restore matrix-mode state.
114 if (newMatrixMode != matrixMode[0])
115 gl.glMatrixMode(matrixMode[0]);
117 this.modelView = modelView;
118 this.projection = projection;
121 protected void validateDrawContext(DrawContext dc)
123 if (dc == null)
125 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
126 WorldWind.logger().log(java.util.logging.Level.FINE, message);
127 throw new IllegalArgumentException(message);
130 if (dc.getGL() == null)
132 String message = WorldWind.retrieveErrMsg("AbstractView.DrawingContextGLIsNull");
133 WorldWind.logger().log(java.util.logging.Level.FINE, message);
134 throw new IllegalStateException(message);
138 public Matrix4 getModelViewMatrix()
140 return this.modelView;
143 public Matrix4 getProjectionMatrix()
145 return this.projection;
148 public java.awt.Rectangle getViewport()
150 return this.viewport;
153 public Frustum getFrustumInModelCoordinates()
155 if (this.frustumInModelCoords == null)
157 // Compute the current model-view coordinate frustum.
158 Frustum frust = this.getFrustum();
159 if (frust != null && this.modelView != null)
160 this.frustumInModelCoords = frust.getInverseTransformed(this.modelView);
162 return this.frustumInModelCoords;
165 public Angle getFieldOfView()
167 return this.fieldOfView;
170 public void setFieldOfView(Angle newFov)
172 if (newFov == null)
174 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
175 WorldWind.logger().log(java.util.logging.Level.FINE, message);
176 throw new IllegalArgumentException(message);
178 this.fieldOfView = newFov;
181 public void pushReferenceCenter(DrawContext dc, Point referenceCenter)
183 validateDrawContext(dc);
184 if (referenceCenter == null)
186 String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
187 WorldWind.logger().log(java.util.logging.Level.FINE, message);
188 throw new IllegalArgumentException(message);
191 Matrix4 newModelView;
192 if (this.modelView != null)
194 newModelView = new Matrix4(this.modelView.getEntries());
195 Matrix4 reference = new Matrix4();
196 reference.translate(referenceCenter);
197 newModelView.multiply(reference);
199 else
201 newModelView = new Matrix4();
204 GL gl = dc.getGL();
205 // Store the current matrix-mode state.
206 gl.glGetIntegerv(GL.GL_MATRIX_MODE, matrixMode, 0);
207 // Push and load a new model-view matrix to the current OpenGL context held by 'dc'.
208 if (matrixMode[0] != GL.GL_MODELVIEW)
209 gl.glMatrixMode(GL.GL_MODELVIEW);
210 gl.glPushMatrix();
211 gl.glLoadMatrixd(newModelView.getEntries(), 0);
212 // Restore matrix-mode state.
213 if (matrixMode[0] != GL.GL_MODELVIEW)
214 gl.glMatrixMode(matrixMode[0]);
217 public void popReferenceCenter(DrawContext dc)
219 validateDrawContext(dc);
220 GL gl = dc.getGL();
221 // Store the current matrix-mode state.
222 gl.glGetIntegerv(GL.GL_MATRIX_MODE, matrixMode, 0);
223 // Pop a model-view matrix off the current OpenGL context held by 'dc'.
224 if (matrixMode[0] != GL.GL_MODELVIEW)
225 gl.glMatrixMode(GL.GL_MODELVIEW);
226 gl.glPopMatrix();
227 // Restore matrix-mode state.
228 if (matrixMode[0] != GL.GL_MODELVIEW)
229 gl.glMatrixMode(matrixMode[0]);
232 public Point getEyePoint()
234 if (this.eye == null)
236 Matrix modelViewInv;
237 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
238 this.eye = modelViewInv.transform(new Point(0, 0, 0, 1));
240 return this.eye;
243 public Point getUpVector()
245 if (this.up == null)
247 Matrix modelViewInv;
248 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
249 this.up = modelViewInv.transform(new Point(0, 1, 0, 0));
251 return this.up;
254 public Point getForwardVector()
256 if (this.forward == null)
258 Matrix modelViewInv;
259 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
260 this.forward = modelViewInv.transform(new Point(0, 0, -1, 0));
262 return this.forward;
265 // TODO: this should be expressed in OpenGL screen coordinates, not toolkit (e.g. AWT) coordinates
266 public Line computeRayFromScreenPoint(double x, double y)
268 if (this.viewport == null)
269 return null;
270 double yInv = this.viewport.height - y - 1; // TODO: should be computed by caller
271 Point a = this.unProject(new Point(x, yInv, 0, 0));
272 Point b = this.unProject(new Point(x, yInv, 1, 0));
273 if (a == null || b == null)
274 return null;
275 return new Line(a, b.subtract(a).normalize());
278 // TODO: rename?, remove?
279 public Position computePositionFromScreenPoint(double x, double y)
281 Line line = this.computeRayFromScreenPoint(x, y);
282 if (line == null)
283 return null;
284 if (this.globe == null)
285 return null;
286 return this.globe.getIntersectionPosition(line);
289 public double computePixelSizeAtDistance(double distance)
291 if (this.pixelSizeScale < 0)
293 // Compute the current coefficient for computing the size of a pixel.
294 if (this.fieldOfView != null && this.viewport.width > 0)
295 this.pixelSizeScale = 2 * fieldOfView.tanHalfAngle() / (double) this.viewport.width;
296 else if (this.viewport.width > 0)
297 this.pixelSizeScale = 1 / (double) this.viewport.width;
299 if (this.pixelSizeScale < 0)
300 return -1;
301 return this.pixelSizeScale * Math.abs(distance);
304 public double computeHorizonDistance()
306 if (this.horizonDistance < 0)
308 this.horizonDistance = this.computeHorizonDistance(this.globe, this.verticalExaggeration,
309 this.getEyePoint());
311 return this.horizonDistance;
314 protected double computeHorizonDistance(Globe globe, double verticalExaggeration, Point eyePoint)
316 if (globe == null || eyePoint == null)
317 return -1;
319 // Compute the current (approximate) distance from eye to globe horizon.
320 Position eyePosition = globe.computePositionFromPoint(eyePoint);
321 double elevation = verticalExaggeration
322 * globe.getElevation(eyePosition.getLatitude(), eyePosition.getLongitude());
323 Point surface = globe.computePointFromPosition(eyePosition.getLatitude(), eyePosition.getLongitude(),
324 elevation);
325 double altitude = eyePoint.length() - surface.length();
326 double radius = globe.getMaximumRadius();
327 return Math.sqrt(altitude * (2 * radius + altitude));
330 public Point project(Point modelPoint)
332 if (modelPoint == null)
334 String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
335 WorldWind.logger().log(java.util.logging.Level.FINE, message);
336 throw new IllegalArgumentException(message);
338 if (this.modelView == null || this.projection == null || this.viewport == null)
339 return null;
340 Point eyeCoord = this.modelView.transform(new Point(modelPoint.x(), modelPoint.y(), modelPoint.z(), 1));
341 Point clipCoord = this.projection.transform(eyeCoord);
342 if (clipCoord.w() == 0)
343 return null;
344 Point normDeviceCoord = new Point(clipCoord.x() / clipCoord.w(), clipCoord.y() / clipCoord.w(),
345 clipCoord.z() / clipCoord.w(), 0);
346 return new Point(
347 (normDeviceCoord.x() + 1) * (this.viewport.width / 2d) + this.viewport.x,
348 (normDeviceCoord.y() + 1) * (this.viewport.height / 2d) + this.viewport.y,
349 (normDeviceCoord.z() + 1) / 2d,
353 public Point unProject(Point windowPoint)
355 if (windowPoint == null)
357 String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
358 WorldWind.logger().log(java.util.logging.Level.FINE, message);
359 throw new IllegalArgumentException(message);
362 if (this.modelView == null || this.projection == null || this.viewport == null)
363 return null;
364 double[] projectionMatrix = this.projection.getEntries();
365 double[] modelViewMatrix = this.modelView.getEntries();
366 int[] viewport = new int[] {this.viewport.x, this.viewport.y, this.viewport.width, this.viewport.height};
367 double[] modelPoint = new double[3];
368 GLU glu = new GLU();
369 if (glu.gluUnProject(windowPoint.x(), windowPoint.y(), windowPoint.z(), modelViewMatrix, 0, projectionMatrix, 0,
370 viewport, 0, modelPoint, 0))
371 return new Point(modelPoint[0], modelPoint[1], modelPoint[2], 0d);
372 else
373 return null;
375 // TODO: uncomment this when Matrix4.getInverse() is fixed
376 // if (projection == null || modelView == null || viewport == null)
377 // return null;
378 // Point ndCoord = new Point(
379 // 2 * (windowPoint.x() - viewport.getX()) / (double) viewport.width - 1,
380 // 2 * (windowPoint.y() - viewport.getY()) / (double) viewport.height - 1,
381 // 2 * windowPoint.z() - 1,
382 // 1);
383 // Matrix m = new Matrix4(modelView.getEntries());
384 // m.multiply(projection);
385 // Point clipCoord = m.getInverse().transform(ndCoord);
386 // if (clipCoord.w() == 0)
387 // return null;
388 // return new Point(clipCoord.x() / clipCoord.w(), clipCoord.y() / clipCoord.w(),
389 // clipCoord.z() / clipCoord.w(), 0);