Update to Worldwind release 0.4.1
[worldwind-tracker.git] / gov / nasa / worldwind / view / AbstractView.java
blobc64b43f734d9eeb54c2ded0a2867229f8d1eb0a2
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 3528 2007-11-15 16:49:06Z 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)
82 this.initialize(dc);
83 this.isInitialized = true;
87 private int getMatrixMode(GL gl)
89 gl.glGetIntegerv(GL.GL_MATRIX_MODE, this.matrixModeArray, 0);
90 return this.matrixModeArray[0];
93 protected void loadModelViewProjection(DrawContext dc, Matrix modelView, Matrix projection)
95 if (dc == null)
97 String message = Logging.getMessage("nullValue.DrawContextIsNull");
98 Logging.logger().severe(message);
99 throw new IllegalArgumentException(message);
102 if (dc.getGL() == null)
104 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
105 Logging.logger().severe(message);
106 throw new IllegalStateException(message);
109 if (modelView == null)
111 Logging.logger().fine("nullValue.ModelViewIsNull");
114 if (projection == null)
116 Logging.logger().fine("nullValue.ProjectionIsNull");
119 this.modelView = modelView;
120 this.projection = projection;
122 GL gl = dc.getGL();
123 // Store the current matrix-mode state.
124 final int matrixMode = this.getMatrixMode(gl);
126 // Apply the model-view matrix to the current OpenGL context held by 'dc'.
127 gl.glMatrixMode(GL.GL_MODELVIEW);
128 if (this.modelView != null)
130 this.modelView.toArray(this.matrixArray, 0, false);
131 gl.glLoadMatrixd(this.matrixArray, 0);
133 else
135 gl.glLoadIdentity();
138 // Apply the projection matrix to the current OpenGL context held by 'dc'.
139 gl.glMatrixMode(GL.GL_PROJECTION);
140 if (this.projection != null)
142 this.projection.toArray(this.matrixArray, 0, false);
143 gl.glLoadMatrixd(this.matrixArray, 0);
145 else
147 gl.glLoadIdentity();
150 // Restore matrix-mode state.
151 gl.glMatrixMode(matrixMode);
154 protected abstract void doApply(DrawContext dc);
156 public Vec4 project(Vec4 modelPoint)
158 if (modelPoint == null)
160 String message = Logging.getMessage("nullValue.PointIsNull");
161 Logging.logger().fine(message);
162 throw new IllegalArgumentException(message);
165 if (this.modelView == null || this.projection == null || this.viewport == null)
166 return null;
168 this.modelView.toArray(this.matrixArray, 0, false);
169 this.projection.toArray(this.matrixArray, 16, false);
171 if (!this.glu.gluProject(
172 modelPoint.x, modelPoint.y, modelPoint.z,
173 this.matrixArray, 0,
174 this.matrixArray, 16,
175 this.viewportArray, 0,
176 this.vecArray, 0))
178 return null;
181 return Vec4.fromArray3(this.vecArray, 0);
184 public Vec4 unProject(Vec4 windowPoint)
186 if (windowPoint == null)
188 String message = Logging.getMessage("nullValue.PointIsNull");
189 Logging.logger().fine(message);
190 throw new IllegalArgumentException(message);
193 if (this.modelView == null || this.projection == null || this.viewport == null)
194 return null;
196 this.modelView.toArray(this.matrixArray, 0, false);
197 this.projection.toArray(this.matrixArray, 16, false);
199 if (!this.glu.gluUnProject(
200 windowPoint.x, windowPoint.y, windowPoint.z,
201 this.matrixArray, 0,
202 this.matrixArray, 16,
203 this.viewportArray, 0,
204 this.vecArray, 0))
206 return null;
209 return Vec4.fromArray3(this.vecArray, 0);
212 public Matrix pushReferenceCenter(DrawContext dc, Vec4 referenceCenter)
214 if (dc == null)
216 String message = Logging.getMessage("nullValue.DrawContextIsNull");
217 Logging.logger().severe(message);
218 throw new IllegalArgumentException(message);
221 if (dc.getGL() == null)
223 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
224 Logging.logger().severe(message);
225 throw new IllegalStateException(message);
228 if (referenceCenter == null)
230 String message = Logging.getMessage("nullValue.PointIsNull");
231 Logging.logger().severe(message);
232 throw new IllegalArgumentException(message);
235 // Compute a new model-view matrix with origin at referenceCenter.
236 Matrix matrix = null;
237 if (this.modelView != null)
238 matrix = this.modelView.multiply(Matrix.fromTranslation(referenceCenter));
240 GL gl = dc.getGL();
241 // Store the current matrix-mode state.
242 final int matrixMode = this.getMatrixMode(gl);
244 if (matrixMode != GL.GL_MODELVIEW)
245 gl.glMatrixMode(GL.GL_MODELVIEW);
247 // Push and load a new model-view matrix to the current OpenGL context held by 'dc'.
248 gl.glPushMatrix();
249 if (matrix != null)
251 matrix.toArray(this.matrixArray, 0, false);
252 gl.glLoadMatrixd(this.matrixArray, 0);
255 // Restore matrix-mode state.
256 if (matrixMode != GL.GL_MODELVIEW)
257 gl.glMatrixMode(matrixMode);
259 return matrix;
262 public void popReferenceCenter(DrawContext dc)
264 if (dc == null)
266 String message = Logging.getMessage("nullValue.DrawContextIsNull");
267 Logging.logger().severe(message);
268 throw new IllegalArgumentException(message);
271 if (dc.getGL() == null)
273 String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
274 Logging.logger().severe(message);
275 throw new IllegalStateException(message);
278 GL gl = dc.getGL();
279 // Store the current matrix-mode state.
280 final int matrixMode = this.getMatrixMode(gl);
282 // Pop a model-view matrix off the current OpenGL context held by 'dc'.
283 if (matrixMode != GL.GL_MODELVIEW)
284 gl.glMatrixMode(GL.GL_MODELVIEW);
286 // Pop the top model-view matrix.
287 gl.glPopMatrix();
289 // Restore matrix-mode state.
290 if (matrixMode != GL.GL_MODELVIEW)
291 gl.glMatrixMode(matrixMode);
294 public Matrix getModelViewMatrix()
296 return this.modelView;
299 public Matrix getProjectionMatrix()
301 return this.projection;
304 public java.awt.Rectangle getViewport()
306 if (this.viewport == null)
307 return null;
309 return new java.awt.Rectangle(this.viewport);
312 public Frustum getFrustumInModelCoordinates()
314 if (this.frustumInModelCoords == null)
316 // Compute the current model-view coordinate frustum.
317 Frustum frust = this.getFrustum();
318 if (frust != null && this.modelView != null)
319 this.frustumInModelCoords = frust.transformBy(this.modelView.getTranspose());
321 return this.frustumInModelCoords;
324 // ============== Runtime Initialization ======================= //
325 // ============== Runtime Initialization ======================= //
326 // ============== Runtime Initialization ======================= //
328 private boolean isInitialized = false;
330 private void initialize(DrawContext dc)
332 this.fieldOfView = getInitialFieldOfView(this.fieldOfView);
333 this.doInitialize(dc);
336 protected void doInitialize(DrawContext dc)
340 private static Angle getInitialFieldOfView(Angle clientValue)
342 // Use value specified by client.
343 if (clientValue != null)
344 return clientValue;
346 // Use value from configuration.
347 Double configValue = Configuration.getDoubleValue(AVKey.FOV, 45d);
348 if (configValue != null)
349 return Angle.fromDegrees(configValue);
351 // Fallback to zero.
352 return Angle.fromDegrees(45.0);
355 // ============== Attribute Accessors ======================= //
356 // ============== Attribute Accessors ======================= //
357 // ============== Attribute Accessors ======================= //
359 // Current DrawContext state.
360 private DrawContext drawContext = null;
361 // Cached viewing attribute computations.
362 private Vec4 eye = null;
363 private Vec4 up = null;
364 private Vec4 forward = null;
365 private Position eyePos = null;
366 private Frustum frustumInModelCoords = null;
367 private Angle fieldOfView = null;
368 private double pixelSizeScale = -1;
369 private double horizonDistance = -1;
371 private void clearAttributes()
373 this.drawContext = null;
374 this.eye = null;
375 this.up = null;
376 this.forward = null;
377 this.eyePos = null;
378 this.frustumInModelCoords = null;
379 this.pixelSizeScale = -1;
380 this.horizonDistance = -1;
383 protected DrawContext getDrawContext()
385 return this.drawContext;
388 public Angle getFieldOfView()
390 return this.fieldOfView;
393 public void setFieldOfView(Angle newFov)
395 if (newFov == null)
397 String message = Logging.getMessage("nullValue.AngleIsNull");
398 Logging.logger().severe(message);
399 throw new IllegalArgumentException(message);
402 this.fieldOfView = newFov;
405 public Vec4 getEyePoint()
407 if (this.eye == null)
409 Matrix modelViewInv;
410 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
411 this.eye = Vec4.UNIT_W.transformBy4(modelViewInv);
413 return this.eye;
416 public Position getEyePosition()
418 if (this.eyePos == null)
420 if (this.drawContext != null && this.drawContext.getGlobe() != null)
422 Vec4 eyeVec = this.getEyePoint();
423 if (eyeVec != null)
424 this.eyePos = this.drawContext.getGlobe().computePositionFromPoint(eyeVec);
427 return this.eyePos;
430 public Vec4 getUpVector()
432 if (this.up == null)
434 Matrix modelViewInv;
435 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
436 this.up = Vec4.UNIT_Y.transformBy4(modelViewInv);
438 return this.up;
441 public Vec4 getForwardVector()
443 if (this.forward == null)
445 Matrix modelViewInv;
446 if (this.modelView != null && (modelViewInv = this.modelView.getInverse()) != null)
447 this.forward = Vec4.UNIT_NEGATIVE_Z.transformBy4(modelViewInv);
449 return this.forward;
452 // ============== Utilities ======================= //
453 // ============== Utilities ======================= //
454 // ============== Utilities ======================= //
456 private final GLU glu = new GLU();
458 // TODO: this should be expressed in OpenGL screen coordinates, not toolkit (e.g. AWT) coordinates
459 public Line computeRayFromScreenPoint(double x, double y)
461 if (this.viewport == null)
462 return null;
464 double invY = this.viewport.height - y - 1; // TODO: should be computed by caller
465 Vec4 a = this.unProject(new Vec4(x, invY, 0, 0));
466 Vec4 b = this.unProject(new Vec4(x, invY, 1, 0));
467 if (a == null || b == null)
468 return null;
470 return new Line(a, b.subtract3(a).normalize3());
473 public Position computePositionFromScreenPoint(double x, double y)
475 if (this.drawContext == null)
476 return null;
478 Globe globe = this.drawContext.getGlobe();
479 if (globe == null)
480 return null;
482 Line line = this.computeRayFromScreenPoint(x, y);
483 if (line == null)
484 return null;
486 return globe.getIntersectionPosition(line);
489 public double computePixelSizeAtDistance(double distance)
491 if (this.pixelSizeScale < 0)
493 // Compute the current coefficient for computing the size of a pixel.
494 if (this.fieldOfView != null && this.viewport.width > 0)
495 this.pixelSizeScale = 2 * this.fieldOfView.tanHalfAngle() / (double) this.viewport.width;
496 else if (this.viewport.width > 0)
497 this.pixelSizeScale = 1 / (double) this.viewport.width;
499 if (this.pixelSizeScale < 0)
500 return -1;
501 return this.pixelSizeScale * Math.abs(distance);
504 public double computeHorizonDistance()
506 if (this.horizonDistance < 0)
508 if (this.drawContext == null)
509 return this.horizonDistance;
511 double altitude = this.computeHeightAboveGlobe(this.drawContext, this.getEyePoint());
512 this.horizonDistance = this.computeHorizonDistance(this.drawContext, altitude);
514 return this.horizonDistance;
517 protected double computeHorizonDistance(DrawContext dc, double altitude)
519 Globe globe = dc.getGlobe();
520 if (globe == null)
521 return -1;
523 if (altitude <= 0.0)
524 return 0;
526 // Compute the (approximate) distance from eye point to globe horizon.
527 double radius = globe.getMaximumRadius();
528 return Math.sqrt(altitude * (2 * radius + altitude));
531 protected double computeHeightAboveGlobe(DrawContext dc, Vec4 point)
533 Globe globe = dc.getGlobe();
534 if (globe == null)
535 return 0;
537 Position globePos = globe.computePositionFromPoint(point);
538 return globePos.getElevation();
541 protected double computeHeightAboveSurface(DrawContext dc, Vec4 point)
543 Globe globe = dc.getGlobe();
544 if (globe == null)
545 return 0;
547 Position globePos = globe.computePositionFromPoint(point);
548 double elevation = globe.getElevation(globePos.getLatitude(), globePos.getLongitude())
549 * dc.getVerticalExaggeration();
551 return globePos.getElevation() - elevation;
554 protected double computeNearClipDistance(DrawContext dc, Vec4 eyeVec)
556 if (dc == null)
558 String message = Logging.getMessage("nullValue.DrawContextIsNull");
559 Logging.logger().severe(message);
560 throw new IllegalArgumentException(message);
563 if (eyeVec == null)
565 String message = Logging.getMessage("nullValue.Vec4IsNull");
566 Logging.logger().severe(message);
567 throw new IllegalArgumentException(message);
570 Angle fov = this.getFieldOfView();
571 if (fov == null)
573 String message = Logging.getMessage("nullValue.AngleIsNull");
574 Logging.logger().severe(message);
575 throw new IllegalArgumentException(message);
578 double heightAboveGlobe = this.computeHeightAboveGlobe(dc, eyeVec);
579 double tanHalfFov = fov.tanHalfAngle();
580 // Compute the most distant near clipping plane.
581 double nearClipDist = heightAboveGlobe / (2 * Math.sqrt(2 * tanHalfFov * tanHalfFov + 1));
583 if (nearClipDist < MIN_NEAR_CLIP_DISTANCE)
584 return MIN_NEAR_CLIP_DISTANCE;
585 return nearClipDist;
588 protected double computeFarClipDistance(DrawContext dc, Vec4 eyeVec)
590 if (dc == null)
592 String message = Logging.getMessage("nullValue.DrawContextIsNull");
593 Logging.logger().severe(message);
594 throw new IllegalArgumentException(message);
597 if (eyeVec == null)
599 String message = Logging.getMessage("nullValue.Vec4IsNull");
600 Logging.logger().severe(message);
601 throw new IllegalArgumentException(message);
604 double heightAboveGlobe = this.computeHeightAboveGlobe(dc, eyeVec);
605 double heightAboveSurface = this.computeHeightAboveSurface(dc, eyeVec);
606 double farClipDist = Math.max(
607 this.computeHorizonDistance(dc, heightAboveGlobe),
608 this.computeHorizonDistance(dc, heightAboveSurface));
610 if (farClipDist < MIN_FAR_CLIP_DISTANCE)
611 return MIN_FAR_CLIP_DISTANCE;
612 return farClipDist;
615 // public LatLon computeVisibleLatLonRange()
616 // {
617 // return null; // TODO
618 // }
620 // ============== State Iterating ======================= //
621 // ============== State Iterating ======================= //
622 // ============== State Iterating ======================= //
624 private ViewStateIterator viewStateIterator = null;
626 public void applyStateIterator(ViewStateIterator viewStateIterator)
628 if (viewStateIterator == null)
630 this.stopStateIterators();
631 return;
634 this.viewStateIterator = viewStateIterator.coalesceWith(this, this.viewStateIterator);
635 this.firePropertyChange(AVKey.VIEW, null, this);
638 public boolean hasStateIterator()
640 return this.viewStateIterator != null;
643 public void stopStateIterators()
645 this.viewStateIterator = null;
648 private void updateStateIterator()
650 if (!this.hasStateIterator())
651 return;
653 if (this.viewStateIterator.hasNextState(this))
655 this.viewStateIterator.nextState(this);
656 this.firePropertyChange(AVKey.VIEW, null, this);
658 else
660 this.stopStateIterators();
661 this.fireViewQuiet();
665 private void fireViewQuiet()
667 this.firePropertyChange(AVKey.VIEW_QUIET, null, this);