Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / view / AbstractView.java
blob59f91bf44e556ee687e0ba0a89785a19c7afff73
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.view;
8 import gov.nasa.worldwind.*;
9 import gov.nasa.worldwind.avlist.AVKey;
10 import gov.nasa.worldwind.geom.*;
11 import gov.nasa.worldwind.globes.Globe;
12 import gov.nasa.worldwind.render.DrawContext;
13 import gov.nasa.worldwind.util.Logging;
15 import javax.media.opengl.GL;
16 import javax.media.opengl.glu.GLU;
18 /**
19 * @author Paul Collins
20 * @version $Id: AbstractView.java 2491 2007-08-02 18:48:00Z dcollins $
22 public abstract class AbstractView extends WWObjectImpl implements View
24 // ============== Viewing State ======================= //
25 // ============== Viewing State ======================= //
26 // ============== Viewing State ======================= //
28 private Matrix modelView;
29 private Matrix projection;
30 private java.awt.Rectangle viewport;
31 private final double[] matrixArray = new double[32];
32 private final double[] vecArray = new double[4];
33 private final int[] viewportArray = new int[4];
34 private final int[] matrixModeArray = new int[1];
36 private static final double MIN_NEAR_CLIP_DISTANCE = 10;
37 private static final double MIN_FAR_CLIP_DISTANCE = 100;
39 public void apply(DrawContext dc)
41 if (dc == null)
43 String message = Logging.getMessage("nullValue.DrawContextIsNull");
44 Logging.logger().severe(message);
45 throw new IllegalArgumentException(message);
48 if (dc.getGL() == null)
50 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
51 Logging.logger().severe(message);
52 throw new IllegalStateException(message);
55 if (dc.getGlobe() == null)
57 String message = Logging.getMessage("layers.AbstractLayer.NoGlobeSpecifiedInDrawingContext");
58 Logging.logger().severe(message);
59 throw new IllegalStateException(message);
62 this.updateViewingState(dc);
63 this.updateStateIterator();
64 this.doApply(dc);
67 private void updateViewingState(DrawContext dc)
69 this.clearAttributes();
70 this.drawContext = dc;
72 // Get the current OpenGL viewport state.
73 dc.getGL().glGetIntegerv(GL.GL_VIEWPORT, this.viewportArray, 0);
74 this.viewport = new java.awt.Rectangle(
75 this.viewportArray[0],
76 this.viewportArray[1],
77 this.viewportArray[2],
78 this.viewportArray[3]);
80 if (!this.isInitialized)
81 this.initialize(dc);
82 this.isInitialized = true;
85 private int getMatrixMode(GL gl)
87 gl.glGetIntegerv(GL.GL_MATRIX_MODE, this.matrixModeArray, 0);
88 return this.matrixModeArray[0];
91 protected void loadModelViewProjection(DrawContext dc, Matrix modelView, Matrix projection)
93 if (dc == null)
95 String message = Logging.getMessage("nullValue.DrawContextIsNull");
96 Logging.logger().severe(message);
97 throw new IllegalArgumentException(message);
100 if (dc.getGL() == null)
102 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
103 Logging.logger().severe(message);
104 throw new IllegalStateException(message);
107 if (modelView == null)
109 Logging.logger().fine("nullValue.ModelViewIsNull");
112 if (projection == null)
114 Logging.logger().fine("nullValue.ProjectionIsNull");
117 this.modelView = modelView;
118 this.projection = projection;
120 GL gl = dc.getGL();
121 // Store the current matrix-mode state.
122 final int matrixMode = this.getMatrixMode(gl);
124 // Apply the model-view matrix to the current OpenGL context held by 'dc'.
125 this.modelView.toArray(this.matrixArray, 0, false);
126 gl.glMatrixMode(GL.GL_MODELVIEW);
127 gl.glLoadMatrixd(this.matrixArray, 0);
129 // Apply the projection matrix to the current OpenGL context held by 'dc'.
130 this.projection.toArray(this.matrixArray, 0, false);
131 gl.glMatrixMode(GL.GL_PROJECTION);
132 gl.glLoadMatrixd(this.matrixArray, 0);
134 // Restore matrix-mode state.
135 gl.glMatrixMode(matrixMode);
138 protected abstract void doApply(DrawContext dc);
140 public Vec4 project(Vec4 modelPoint)
142 if (modelPoint == null)
144 String message = Logging.getMessage("nullValue.PointIsNull");
145 Logging.logger().fine(message);
146 throw new IllegalArgumentException(message);
149 if (this.modelView == null || this.projection == null || this.viewport == null)
150 return null;
152 this.modelView.toArray(this.matrixArray, 0, false);
153 this.projection.toArray(this.matrixArray, 16, false);
155 if (!this.glu.gluProject(
156 modelPoint.x, modelPoint.y, modelPoint.z,
157 this.matrixArray, 0,
158 this.matrixArray, 16,
159 this.viewportArray, 0,
160 this.vecArray, 0))
162 return null;
165 return Vec4.fromArray3(this.vecArray, 0);
168 public Vec4 unProject(Vec4 windowPoint)
170 if (windowPoint == null)
172 String message = Logging.getMessage("nullValue.PointIsNull");
173 Logging.logger().fine(message);
174 throw new IllegalArgumentException(message);
177 if (this.modelView == null || this.projection == null || this.viewport == null)
178 return null;
180 this.modelView.toArray(this.matrixArray, 0, false);
181 this.projection.toArray(this.matrixArray, 16, false);
183 if (!this.glu.gluUnProject(
184 windowPoint.x, windowPoint.y, windowPoint.z,
185 this.matrixArray, 0,
186 this.matrixArray, 16,
187 this.viewportArray, 0,
188 this.vecArray, 0))
190 return null;
193 return Vec4.fromArray3(this.vecArray, 0);
196 public Matrix pushReferenceCenter(DrawContext dc, Vec4 referenceCenter)
198 if (dc == null)
200 String message = Logging.getMessage("nullValue.DrawContextIsNull");
201 Logging.logger().severe(message);
202 throw new IllegalArgumentException(message);
205 if (dc.getGL() == null)
207 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
208 Logging.logger().severe(message);
209 throw new IllegalStateException(message);
212 if (referenceCenter == null)
214 String message = Logging.getMessage("nullValue.PointIsNull");
215 Logging.logger().fine(message);
216 throw new IllegalArgumentException(message);
219 // Compute a new model-view matrix with origin at referenceCenter.
220 Matrix matrix = null;
221 if (this.modelView != null)
222 matrix = this.modelView.multiply(Matrix.fromTranslation(referenceCenter));
224 GL gl = dc.getGL();
225 // Store the current matrix-mode state.
226 final int matrixMode = this.getMatrixMode(gl);
228 if (matrixMode != GL.GL_MODELVIEW)
229 gl.glMatrixMode(GL.GL_MODELVIEW);
231 // Push and load a new model-view matrix to the current OpenGL context held by 'dc'.
232 gl.glPushMatrix();
233 if (matrix != null)
235 matrix.toArray(this.matrixArray, 0, false);
236 gl.glLoadMatrixd(this.matrixArray, 0);
239 // Restore matrix-mode state.
240 if (matrixMode != GL.GL_MODELVIEW)
241 gl.glMatrixMode(matrixMode);
243 return matrix;
246 public void popReferenceCenter(DrawContext dc)
248 if (dc == null)
250 String message = Logging.getMessage("nullValue.DrawContextIsNull");
251 Logging.logger().severe(message);
252 throw new IllegalArgumentException(message);
255 if (dc.getGL() == null)
257 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
258 Logging.logger().severe(message);
259 throw new IllegalStateException(message);
262 GL gl = dc.getGL();
263 // Store the current matrix-mode state.
264 final int matrixMode = this.getMatrixMode(gl);
266 // Pop a model-view matrix off the current OpenGL context held by 'dc'.
267 if (matrixMode != GL.GL_MODELVIEW)
268 gl.glMatrixMode(GL.GL_MODELVIEW);
270 // Pop the top model-view matrix.
271 gl.glPopMatrix();
273 // Restore matrix-mode state.
274 if (matrixMode != GL.GL_MODELVIEW)
275 gl.glMatrixMode(matrixMode);
278 public Matrix getModelViewMatrix()
280 return this.modelView;
283 public Matrix getProjectionMatrix()
285 return this.projection;
288 public java.awt.Rectangle getViewport()
290 return new java.awt.Rectangle(this.viewport);
293 public Frustum getFrustumInModelCoordinates()
295 if (this.frustumInModelCoords == null)
297 // Compute the current model-view coordinate frustum.
298 Frustum frust = this.getFrustum();
299 if (frust != null && this.modelView != null)
300 this.frustumInModelCoords = frust.transformBy(this.modelView.getTranspose());
302 return this.frustumInModelCoords;
305 // ============== Runtime Initialization ======================= //
306 // ============== Runtime Initialization ======================= //
307 // ============== Runtime Initialization ======================= //
309 private boolean isInitialized = false;
311 private void initialize(DrawContext dc)
313 this.fieldOfView = getInitialFieldOfView(this.fieldOfView);
314 this.doInitialize(dc);
317 protected void doInitialize(DrawContext dc)
321 private static Angle getInitialFieldOfView(Angle clientValue)
323 // Use value specified by client.
324 if (clientValue != null)
325 return clientValue;
327 // Use value from configuration.
328 Double configValue = Configuration.getDoubleValue(AVKey.FOV, 45d);
329 if (configValue != null)
330 return Angle.fromDegrees(configValue);
332 // Fallback to zero.
333 return Angle.fromDegrees(45.0);
336 // ============== Attribute Accessors ======================= //
337 // ============== Attribute Accessors ======================= //
338 // ============== Attribute Accessors ======================= //
340 // Current DrawContext state.
341 private DrawContext drawContext = null;
342 // Cached viewing attribute computations.
343 private Vec4 eye = null;
344 private Vec4 up = null;
345 private Vec4 forward = null;
346 private Position eyePos = null;
347 private Frustum frustumInModelCoords = null;
348 private Angle fieldOfView = null;
349 private double pixelSizeScale = -1;
350 private double horizonDistance = -1;
352 private void clearAttributes()
354 this.drawContext = null;
355 this.eye = null;
356 this.up = null;
357 this.forward = null;
358 this.eyePos = null;
359 this.frustumInModelCoords = null;
360 this.pixelSizeScale = -1;
361 this.horizonDistance = -1;
364 protected DrawContext getDrawContext()
366 return this.drawContext;
369 public Angle getFieldOfView()
371 return this.fieldOfView;
374 public void setFieldOfView(Angle newFov)
376 if (newFov == null)
378 String message = Logging.getMessage("nullValue.AngleIsNull");
379 Logging.logger().severe(message);
380 throw new IllegalArgumentException(message);
382 this.fieldOfView = newFov;
385 public Vec4 getEyePoint()
387 if (this.eye == null)
389 Matrix modelViewInv;
390 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
391 this.eye = Vec4.UNIT_W.transformBy4(modelViewInv);
393 return this.eye;
396 public Position getEyePosition()
398 if (this.eyePos == null)
400 if (this.drawContext != null && this.drawContext.getGlobe() != null)
402 Vec4 eyeVec = this.getEyePoint();
403 if (eyeVec != null)
404 this.eyePos = this.drawContext.getGlobe().computePositionFromPoint(eyeVec);
407 return this.eyePos;
410 public Vec4 getUpVector()
412 if (this.up == null)
414 Matrix modelViewInv;
415 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
416 this.up = Vec4.UNIT_Y.transformBy4(modelViewInv);
418 return this.up;
421 public Vec4 getForwardVector()
423 if (this.forward == null)
425 Matrix modelViewInv;
426 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
427 this.forward = Vec4.UNIT_NEGATIVE_Z.transformBy4(modelViewInv);
429 return this.forward;
432 // ============== Utilities ======================= //
433 // ============== Utilities ======================= //
434 // ============== Utilities ======================= //
436 private final GLU glu = new GLU();
438 // TODO: this should be expressed in OpenGL screen coordinates, not toolkit (e.g. AWT) coordinates
439 public Line computeRayFromScreenPoint(double x, double y)
441 if (this.viewport == null)
442 return null;
444 double invY = this.viewport.height - y - 1; // TODO: should be computed by caller
445 Vec4 a = this.unProject(new Vec4(x, invY, 0, 0));
446 Vec4 b = this.unProject(new Vec4(x, invY, 1, 0));
447 if (a == null || b == null)
448 return null;
450 return new Line(a, b.subtract3(a).normalize3());
453 public Position computePositionFromScreenPoint(double x, double y)
455 if (this.drawContext == null)
456 return null;
458 Globe globe = this.drawContext.getGlobe();
459 if (globe == null)
460 return null;
462 Line line = this.computeRayFromScreenPoint(x, y);
463 if (line == null)
464 return null;
466 return globe.getIntersectionPosition(line);
469 public double computePixelSizeAtDistance(double distance)
471 if (this.pixelSizeScale < 0)
473 // Compute the current coefficient for computing the size of a pixel.
474 if (this.fieldOfView != null && this.viewport.width > 0)
475 this.pixelSizeScale = 2 * this.fieldOfView.tanHalfAngle() / (double) this.viewport.width;
476 else if (this.viewport.width > 0)
477 this.pixelSizeScale = 1 / (double) this.viewport.width;
479 if (this.pixelSizeScale < 0)
480 return -1;
481 return this.pixelSizeScale * Math.abs(distance);
484 public double computeHorizonDistance()
486 if (this.horizonDistance < 0)
488 if (this.drawContext == null)
489 return this.horizonDistance;
491 double altitude = this.computeHeightAboveGlobe(this.drawContext, this.getEyePoint());
492 this.horizonDistance = this.computeHorizonDistance(this.drawContext, altitude);
494 return this.horizonDistance;
497 protected double computeHorizonDistance(DrawContext dc, double altitude)
499 Globe globe = dc.getGlobe();
500 if (globe == null)
501 return -1;
503 if (altitude <= 0.0)
504 return 0;
506 // Compute the (approximate) distance from eye point to globe horizon.
507 double radius = globe.getMaximumRadius();
508 return Math.sqrt(altitude * (2 * radius + altitude));
511 protected double computeHeightAboveGlobe(DrawContext dc, Vec4 point)
513 Globe globe = dc.getGlobe();
514 if (globe == null)
515 return 0;
517 Position globePos = globe.computePositionFromPoint(point);
518 return globePos.getElevation();
521 protected double computeHeightAboveSurface(DrawContext dc, Vec4 point)
523 Globe globe = dc.getGlobe();
524 if (globe == null)
525 return 0;
527 Position globePos = globe.computePositionFromPoint(point);
528 double elevation = globe.getElevation(globePos.getLatitude(), globePos.getLongitude())
529 * dc.getVerticalExaggeration();
531 return globePos.getElevation() - elevation;
534 protected double computeNearClipDistance(DrawContext dc, Vec4 eyeVec)
536 Angle fov = this.getFieldOfView();
537 if (fov == null)
538 return MIN_NEAR_CLIP_DISTANCE;
540 double heightAboveGlobe = this.computeHeightAboveGlobe(dc, eyeVec);
541 double tanHalfFov = fov.tanHalfAngle();
542 // Compute the most distant near clipping plane.
543 double nearClipDist = heightAboveGlobe / (2 * Math.sqrt(2 * tanHalfFov * tanHalfFov + 1));
545 if (nearClipDist < MIN_NEAR_CLIP_DISTANCE)
546 return MIN_NEAR_CLIP_DISTANCE;
547 return nearClipDist;
550 protected double computeFarClipDistance(DrawContext dc, Vec4 eyeVec)
552 double heightAboveGlobe = this.computeHeightAboveGlobe(dc, eyeVec);
553 double heightAboveSurface = this.computeHeightAboveSurface(dc, eyeVec);
554 double farClipDist = Math.max(
555 this.computeHorizonDistance(dc, heightAboveGlobe),
556 this.computeHorizonDistance(dc, heightAboveSurface));
558 if (farClipDist < MIN_FAR_CLIP_DISTANCE)
559 return MIN_FAR_CLIP_DISTANCE;
560 return farClipDist;
563 // public LatLon computeVisibleLatLonRange()
564 // {
565 // return null; // TODO
566 // }
568 // ============== State Iterating ======================= //
569 // ============== State Iterating ======================= //
570 // ============== State Iterating ======================= //
572 private ViewStateIterator viewStateIterator = null;
574 public void applyStateIterator(ViewStateIterator viewStateIterator)
576 if (viewStateIterator == null)
578 this.stopStateIterators();
579 return;
582 this.viewStateIterator = viewStateIterator.coalesceWith(this, this.viewStateIterator);
583 this.firePropertyChange(AVKey.VIEW, null, this);
586 public boolean hasStateIterator()
588 return this.viewStateIterator != null;
591 public void stopStateIterators()
593 this.viewStateIterator = null;
596 private void updateStateIterator()
598 if (!this.hasStateIterator())
599 return;
601 if (this.viewStateIterator.hasNextState(this))
603 this.viewStateIterator.nextState(this);
604 this.firePropertyChange(AVKey.VIEW, null, this);
606 else
608 this.stopStateIterators();