2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
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
;
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
)
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();
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
)
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
)
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
;
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);
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);
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)
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
,
174 this.matrixArray
, 16,
175 this.viewportArray
, 0,
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)
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
,
202 this.matrixArray
, 16,
203 this.viewportArray
, 0,
209 return Vec4
.fromArray3(this.vecArray
, 0);
212 public Matrix
pushReferenceCenter(DrawContext dc
, Vec4 referenceCenter
)
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
));
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'.
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
);
262 public void popReferenceCenter(DrawContext dc
)
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
);
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.
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)
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)
346 // Use value from configuration.
347 Double configValue
= Configuration
.getDoubleValue(AVKey
.FOV
, 45d
);
348 if (configValue
!= null)
349 return Angle
.fromDegrees(configValue
);
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;
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
)
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)
410 if (this.modelView
!= null && (modelViewInv
= this.modelView
.getInverse()) != null)
411 this.eye
= Vec4
.UNIT_W
.transformBy4(modelViewInv
);
416 public Position
getEyePosition()
418 if (this.eyePos
== null)
420 if (this.drawContext
!= null && this.drawContext
.getGlobe() != null)
422 Vec4 eyeVec
= this.getEyePoint();
424 this.eyePos
= this.drawContext
.getGlobe().computePositionFromPoint(eyeVec
);
430 public Vec4
getUpVector()
435 if (this.modelView
!= null && (modelViewInv
= this.modelView
.getInverse()) != null)
436 this.up
= Vec4
.UNIT_Y
.transformBy4(modelViewInv
);
441 public Vec4
getForwardVector()
443 if (this.forward
== null)
446 if (this.modelView
!= null && (modelViewInv
= this.modelView
.getInverse()) != null)
447 this.forward
= Vec4
.UNIT_NEGATIVE_Z
.transformBy4(modelViewInv
);
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)
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)
470 return new Line(a
, b
.subtract3(a
).normalize3());
473 public Position
computePositionFromScreenPoint(double x
, double y
)
475 if (this.drawContext
== null)
478 Globe globe
= this.drawContext
.getGlobe();
482 Line line
= this.computeRayFromScreenPoint(x
, y
);
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)
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();
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();
537 Position globePos
= globe
.computePositionFromPoint(point
);
538 return globePos
.getElevation();
541 protected double computeHeightAboveSurface(DrawContext dc
, Vec4 point
)
543 Globe globe
= dc
.getGlobe();
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
)
558 String message
= Logging
.getMessage("nullValue.DrawContextIsNull");
559 Logging
.logger().severe(message
);
560 throw new IllegalArgumentException(message
);
565 String message
= Logging
.getMessage("nullValue.Vec4IsNull");
566 Logging
.logger().severe(message
);
567 throw new IllegalArgumentException(message
);
570 Angle fov
= this.getFieldOfView();
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
;
588 protected double computeFarClipDistance(DrawContext dc
, Vec4 eyeVec
)
592 String message
= Logging
.getMessage("nullValue.DrawContextIsNull");
593 Logging
.logger().severe(message
);
594 throw new IllegalArgumentException(message
);
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
;
615 // public LatLon computeVisibleLatLonRange()
617 // return null; // TODO
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();
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())
653 if (this.viewStateIterator
.hasNextState(this))
655 this.viewStateIterator
.nextState(this);
656 this.firePropertyChange(AVKey
.VIEW
, null, this);
660 this.stopStateIterators();
661 this.fireViewQuiet();
665 private void fireViewQuiet()
667 this.firePropertyChange(AVKey
.VIEW_QUIET
, null, this);