Worldwind public release 0.2.1
[worldwind-tracker.git] / gov / nasa / worldwind / AbstractView.java
bloba063bcaf2852a02d73a3fa0c911bc7f3a84d30a8
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.*;
10 import javax.media.opengl.*;
11 import javax.media.opengl.glu.*;
12 import java.util.logging.Level;
14 /**
15 * @author Paul Collins
16 * @version $Id: AbstractView.java 2126 2007-06-21 21:00:42Z dcollins $
18 public abstract class AbstractView extends WWObjectImpl implements View
20 // Shared attributes.
21 private boolean isInitialized = false;
22 private final double[] matrixArray = new double[32];
23 private final double[] vecArray = new double[4];
24 private final int[] viewportArray = new int[4];
26 // ============== Viewing State ======================= //
27 // ============== Viewing State ======================= //
28 // ============== Viewing State ======================= //
30 // Current OpenGL viewing state.
31 private Matrix modelView;
32 private Matrix projection;
33 private java.awt.Rectangle viewport;
35 public void apply(DrawContext dc)
37 if (dc == null)
39 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
40 WorldWind.logger().log(java.util.logging.Level.FINE, message);
41 throw new IllegalArgumentException(message);
44 if (dc.getGL() == null)
46 String message = WorldWind.retrieveErrMsg("AbstractView.DrawingContextGLIsNull");
47 WorldWind.logger().log(java.util.logging.Level.FINE, message);
48 throw new IllegalStateException(message);
51 if (dc.getGlobe() == null)
53 String message = WorldWind.retrieveErrMsg("layers.AbstractLayer.NoGlobeSpecifiedInDrawingContext");
54 WorldWind.logger().log(java.util.logging.Level.FINE, message);
55 throw new IllegalStateException(message);
58 if (this.isViewUpdateable(dc))
59 this.updateViewingState(dc);
61 this.doApply(dc);
64 public boolean isViewUpdateable(DrawContext dc)
66 if (dc == null)
68 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
69 WorldWind.logger().log(java.util.logging.Level.FINE, message);
70 throw new IllegalArgumentException(message);
73 return !dc.isPickingMode();
76 private void updateViewingState(DrawContext dc)
78 this.clearAttributes();
79 this.drawContext = dc;
81 // Get the current OpenGL viewport state.
82 dc.getGL().glGetIntegerv(GL.GL_VIEWPORT, this.viewportArray, 0);
83 this.viewport = new java.awt.Rectangle(
84 this.viewportArray[0],
85 this.viewportArray[1],
86 this.viewportArray[2],
87 this.viewportArray[3]);
89 if (!this.isInitialized)
90 this.initialize(dc);
91 this.isInitialized = true;
93 this.updateStateIterators(dc);
96 protected void loadModelViewProjection(DrawContext dc, Matrix modelView, Matrix projection)
98 if (dc == null)
100 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
101 WorldWind.logger().log(java.util.logging.Level.FINE, message);
102 throw new IllegalArgumentException(message);
105 if (dc.getGL() == null)
107 String message = WorldWind.retrieveErrMsg("AbstractView.DrawingContextGLIsNull");
108 WorldWind.logger().log(java.util.logging.Level.FINE, message);
109 throw new IllegalStateException(message);
112 if (modelView == null)
114 String message = WorldWind.retrieveErrMsg("AbstractView.ModelViewIsNull");
115 WorldWind.logger().log(Level.FINE, message);
118 if (projection == null)
120 String message = WorldWind.retrieveErrMsg("AbstractView.ProjectionIsNull");
121 WorldWind.logger().log(Level.FINE, message);
124 this.modelView = modelView;
125 this.projection = projection;
127 GL gl = dc.getGL();
128 // Store the current matrix-mode state.
129 final int matrixMode = this.getMatrixMode(gl);
131 // Apply the model-view matrix to the current OpenGL context held by 'dc'.
132 this.modelView.toArray(this.matrixArray, 0, false);
133 gl.glMatrixMode(GL.GL_MODELVIEW);
134 gl.glLoadMatrixd(this.matrixArray, 0);
136 // Apply the projection matrix to the current OpenGL context held by 'dc'.
137 this.projection.toArray(this.matrixArray, 0, false);
138 gl.glMatrixMode(GL.GL_PROJECTION);
139 gl.glLoadMatrixd(this.matrixArray, 0);
141 // Restore matrix-mode state.
142 gl.glMatrixMode(matrixMode);
145 protected abstract void doApply(DrawContext dc);
147 public Matrix pushReferenceCenter(DrawContext dc, Vec4 referenceCenter)
149 if (dc == null)
151 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
152 WorldWind.logger().log(java.util.logging.Level.FINE, message);
153 throw new IllegalArgumentException(message);
156 if (dc.getGL() == null)
158 String message = WorldWind.retrieveErrMsg("AbstractView.DrawingContextGLIsNull");
159 WorldWind.logger().log(java.util.logging.Level.FINE, message);
160 throw new IllegalStateException(message);
163 if (referenceCenter == null)
165 String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
166 WorldWind.logger().log(Level.FINE, message);
167 throw new IllegalArgumentException(message);
170 // Compute a new model-view matrix with origin at referenceCenter.
171 Matrix matrix = null;
172 if (this.modelView != null)
173 matrix = this.modelView.multiply(Matrix.fromTranslation(referenceCenter));
175 GL gl = dc.getGL();
176 // Store the current matrix-mode state.
177 final int matrixMode = this.getMatrixMode(gl);
179 if (matrixMode != GL.GL_MODELVIEW)
180 gl.glMatrixMode(GL.GL_MODELVIEW);
182 // Push and load a new model-view matrix to the current OpenGL context held by 'dc'.
183 gl.glPushMatrix();
184 if (matrix != null)
186 matrix.toArray(this.matrixArray, 0, false);
187 gl.glLoadMatrixd(this.matrixArray, 0);
190 // Restore matrix-mode state.
191 if (matrixMode != GL.GL_MODELVIEW)
192 gl.glMatrixMode(matrixMode);
194 return matrix;
197 public void popReferenceCenter(DrawContext dc)
199 if (dc == null)
201 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
202 WorldWind.logger().log(java.util.logging.Level.FINE, message);
203 throw new IllegalArgumentException(message);
206 if (dc.getGL() == null)
208 String message = WorldWind.retrieveErrMsg("AbstractView.DrawingContextGLIsNull");
209 WorldWind.logger().log(java.util.logging.Level.FINE, message);
210 throw new IllegalStateException(message);
213 GL gl = dc.getGL();
214 // Store the current matrix-mode state.
215 final int matrixMode = this.getMatrixMode(gl);
217 // Pop a model-view matrix off the current OpenGL context held by 'dc'.
218 if (matrixMode != GL.GL_MODELVIEW)
219 gl.glMatrixMode(GL.GL_MODELVIEW);
221 // Pop the top model-view matrix.
222 gl.glPopMatrix();
224 // Restore matrix-mode state.
225 if (matrixMode != GL.GL_MODELVIEW)
226 gl.glMatrixMode(matrixMode);
229 private final int[] matrixModeArray = new int[1];
231 private int getMatrixMode(GL gl)
233 gl.glGetIntegerv(GL.GL_MATRIX_MODE, this.matrixModeArray, 0);
234 return this.matrixModeArray[0];
237 // ============== Runtime Initialization ======================= //
238 // ============== Runtime Initialization ======================= //
239 // ============== Runtime Initialization ======================= //
241 private void initialize(DrawContext dc)
243 this.fieldOfView = getInitialFieldOfView(dc, this.fieldOfView);
244 this.doInitialize(dc);
247 protected void doInitialize(DrawContext dc)
251 protected static Angle getInitialFieldOfView(DrawContext dc, Angle clientValue)
253 // Use value specified by client.
254 if (clientValue != null)
255 return clientValue;
257 // Use value from configuration.
258 Double configValue = Configuration.getDoubleValue(AVKey.FOV);
259 if (configValue != null)
260 return Angle.fromDegrees(configValue);
262 // Fallback to zero.
263 return Angle.fromDegrees(45.0);
266 protected static Angle getInitialLatitude(DrawContext dc, Angle clientValue)
268 // Use value specified by client.
269 if (clientValue != null)
270 return clientValue;
272 // Use value from configuration.
273 Double configValue = Configuration.getDoubleValue(AVKey.INITIAL_LATITUDE);
274 if (configValue != null)
275 return Angle.fromDegrees(configValue);
277 // Fallback to zero.
278 return Angle.ZERO;
281 protected static Angle getInitialLongitude(DrawContext dc, Angle clientValue)
283 // Use value specified by client.
284 if (clientValue != null)
285 return clientValue;
287 // Use value from configuration.
288 Double configValue = Configuration.getDoubleValue(AVKey.INITIAL_LONGITUDE);
289 if (configValue != null)
290 return Angle.fromDegrees(configValue);
292 // Use longitude of system time-zone.
293 java.util.TimeZone tz = java.util.Calendar.getInstance().getTimeZone();
294 if (tz != null)
295 return Angle.fromDegrees(180.0 * tz.getOffset(System.currentTimeMillis()) / (12.0 * 3.6e6));
297 // Fallback to zero.
298 return Angle.ZERO;
301 protected static Angle getInitialHeading(DrawContext dc, Angle clientValue)
303 // Use value specified by client.
304 if (clientValue != null)
305 return clientValue;
307 // Use value from configuration.
308 Double configValue = Configuration.getDoubleValue("null");
309 if (configValue != null)
310 return Angle.fromDegrees(configValue);
312 // Fallback to zero.
313 return Angle.ZERO;
316 protected static Angle getInitialPitch(DrawContext dc, Angle clientValue)
318 // Use value specified by client.
319 if (clientValue != null)
320 return clientValue;
322 // Use value from configuration.
323 Double configValue = Configuration.getDoubleValue("null");
324 if (configValue != null)
325 return Angle.fromDegrees(configValue);
327 // Fallback to zero.
328 return Angle.ZERO;
331 protected static double getInitialAltitude(DrawContext dc, double clientValue)
333 // Use value specified by client.
334 if (clientValue >= 0.0)
335 return clientValue;
337 // Use value from configuration.
338 Double configValue = Configuration.getDoubleValue("null");
339 if (configValue != null)
340 return configValue;
342 // Use globe radius with coefficient.
343 if (dc != null && dc.getGlobe() != null)
344 return 3.0 * dc.getGlobe().getRadius();
346 // Fallback to zero.
347 return 0.0;
350 // ============== Attribute Accessors ======================= //
351 // ============== Attribute Accessors ======================= //
352 // ============== Attribute Accessors ======================= //
354 // Current DrawContext state.
355 private DrawContext drawContext = null;
356 // Cached viewing attribute computations.
357 private Vec4 eye = null;
358 private Vec4 up = null;
359 private Vec4 forward = null;
360 private Frustum frustumInModelCoords = null;
361 private Angle fieldOfView = null;
362 private double pixelSizeScale = -1;
363 private double horizonDistance = -1;
365 private void clearAttributes()
367 this.drawContext = null;
368 this.eye = null;
369 this.up = null;
370 this.forward = null;
371 this.frustumInModelCoords = null;
372 this.pixelSizeScale = -1;
373 this.horizonDistance = -1;
376 protected DrawContext getDrawContext()
378 return this.drawContext;
381 public Matrix getModelViewMatrix()
383 return this.modelView;
386 public Matrix getProjectionMatrix()
388 return this.projection;
391 public java.awt.Rectangle getViewport()
393 return new java.awt.Rectangle(this.viewport);
396 public Frustum getFrustumInModelCoordinates()
398 if (this.frustumInModelCoords == null)
400 // Compute the current model-view coordinate frustum.
401 Frustum frust = this.getFrustum();
402 if (frust != null && this.modelView != null)
403 this.frustumInModelCoords = frust.transformBy(this.modelView.getTranspose());
405 return this.frustumInModelCoords;
408 public Angle getFieldOfView()
410 return this.fieldOfView;
413 public void setFieldOfView(Angle newFov)
415 if (newFov == null)
417 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
418 WorldWind.logger().log(java.util.logging.Level.FINE, message);
419 throw new IllegalArgumentException(message);
421 this.fieldOfView = newFov;
424 public Vec4 getEyePoint()
426 if (this.eye == null)
428 Matrix modelViewInv;
429 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
430 this.eye = Vec4.UNIT_W.transformBy4(modelViewInv);
432 return this.eye;
435 public Vec4 getUpVector()
437 if (this.up == null)
439 Matrix modelViewInv;
440 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
441 this.up = Vec4.UNIT_Y.transformBy4(modelViewInv);
443 return this.up;
446 public Vec4 getForwardVector()
448 if (this.forward == null)
450 Matrix modelViewInv;
451 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
452 this.forward = Vec4.UNIT_NEGATIVE_Z.transformBy4(modelViewInv);
454 return this.forward;
457 // ============== State Iterators ======================= //
458 // ============== State Iterators ======================= //
459 // ============== State Iterators ======================= //
461 private ViewStateIterator viewStateIterator = null;
463 public void iterateOver(ViewStateIterator viewStateIterator)
465 if (viewStateIterator == null)
467 this.stopIterating();
468 return;
471 this.viewStateIterator = viewStateIterator.coalesceWith(this, this.viewStateIterator);
472 this.firePropertyChange(AVKey.VIEW, null, this);
475 public boolean isIterating()
477 return this.viewStateIterator != null;
480 public void stopIterating()
482 this.viewStateIterator = null;
485 private void updateStateIterators(DrawContext dc)
487 if (!this.isIterating())
488 return;
490 if (this.viewStateIterator.hasNextState(this))
492 this.viewStateIterator.nextState(this);
493 this.firePropertyChange(AVKey.VIEW, null, this);
495 else
497 this.stopIterating();
501 // ============== Utilities ======================= //
502 // ============== Utilities ======================= //
503 // ============== Utilities ======================= //
505 // GLU
506 private final GLU glu = new GLU();
508 // TODO: this should be expressed in OpenGL screen coordinates, not toolkit (e.g. AWT) coordinates
509 public Line computeRayFromScreenPoint(double x, double y)
511 if (this.viewport == null)
512 return null;
513 double invY = this.viewport.height - y - 1; // TODO: should be computed by caller
514 Vec4 a = this.unProject(new Vec4(x, invY, 0, 0));
515 Vec4 b = this.unProject(new Vec4(x, invY, 1, 0));
516 if (a == null || b == null)
517 return null;
518 return new Line(a, b.subtract3(a).normalize3());
521 public Position computePositionFromScreenPoint(double x, double y)
523 Line line = this.computeRayFromScreenPoint(x, y);
524 if (line == null)
525 return null;
527 if (this.drawContext == null)
528 return null;
530 Globe globe = this.drawContext.getGlobe();
531 if (globe == null)
532 return null;
534 return globe.getIntersectionPosition(line);
537 public double computePixelSizeAtDistance(double distance)
539 if (this.pixelSizeScale < 0)
541 // Compute the current coefficient for computing the size of a pixel.
542 if (this.fieldOfView != null && this.viewport.width > 0)
543 this.pixelSizeScale = 2 * this.fieldOfView.tanHalfAngle() / (double) this.viewport.width;
544 else if (this.viewport.width > 0)
545 this.pixelSizeScale = 1 / (double) this.viewport.width;
547 if (this.pixelSizeScale < 0)
548 return -1;
549 return this.pixelSizeScale * Math.abs(distance);
552 public double computeHorizonDistance()
554 if (this.horizonDistance < 0)
556 if (this.drawContext == null)
557 return this.horizonDistance;
559 Globe globe = this.drawContext.getGlobe();
560 if (globe == null)
561 return this.horizonDistance;
563 this.horizonDistance = this.computeHorizonDistance(globe, this.drawContext.getVerticalExaggeration(),
564 this.getEyePoint());
566 return this.horizonDistance;
569 protected double computeHorizonDistance(Globe globe, double verticalExaggeration, Vec4 eyeVec)
571 if (globe == null || eyeVec == null)
572 return -1;
574 // Compute the current (approximate) distance from eye to globe horizon.
575 Position eyePosition = globe.computePositionFromPoint(eyeVec);
576 double elevation = verticalExaggeration
577 * globe.getElevation(eyePosition.getLatitude(), eyePosition.getLongitude());
578 Vec4 surface = globe.computePointFromPosition(eyePosition.getLatitude(), eyePosition.getLongitude(),
579 elevation);
580 double altitude = eyeVec.getLength3() - surface.getLength3();
581 double radius = globe.getMaximumRadius();
582 return Math.sqrt(altitude * (2 * radius + altitude));
585 public Vec4 project(Vec4 modelPoint)
587 if (modelPoint == null)
589 String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
590 WorldWind.logger().log(Level.FINE, message);
591 throw new IllegalArgumentException(message);
594 if (this.modelView == null || this.projection == null || this.viewport == null)
595 return null;
597 this.modelView.toArray(this.matrixArray, 0, false);
598 this.projection.toArray(this.matrixArray, 16, false);
600 if (!this.glu.gluProject(
601 modelPoint.x, modelPoint.y, modelPoint.z,
602 this.matrixArray, 0,
603 this.matrixArray, 16,
604 this.viewportArray, 0,
605 this.vecArray, 0))
607 return null;
610 return Vec4.fromArray3(this.vecArray, 0);
613 public Vec4 unProject(Vec4 windowPoint)
615 if (windowPoint == null)
617 String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
618 WorldWind.logger().log(Level.FINE, message);
619 throw new IllegalArgumentException(message);
622 if (this.modelView == null || this.projection == null || this.viewport == null)
623 return null;
625 this.modelView.toArray(this.matrixArray, 0, false);
626 this.projection.toArray(this.matrixArray, 16, false);
628 if (!this.glu.gluUnProject(
629 windowPoint.x, windowPoint.y, windowPoint.z,
630 this.matrixArray, 0,
631 this.matrixArray, 16,
632 this.viewportArray, 0,
633 this.vecArray, 0))
635 return null;
638 return Vec4.fromArray3(this.vecArray, 0);