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 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
)
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
)
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
)
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
;
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)
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
,
158 this.matrixArray
, 16,
159 this.viewportArray
, 0,
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)
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
,
186 this.matrixArray
, 16,
187 this.viewportArray
, 0,
193 return Vec4
.fromArray3(this.vecArray
, 0);
196 public Matrix
pushReferenceCenter(DrawContext dc
, Vec4 referenceCenter
)
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
));
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'.
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
);
246 public void popReferenceCenter(DrawContext dc
)
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
);
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.
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)
327 // Use value from configuration.
328 Double configValue
= Configuration
.getDoubleValue(AVKey
.FOV
, 45d
);
329 if (configValue
!= null)
330 return Angle
.fromDegrees(configValue
);
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;
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
)
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)
390 if (this.modelView
!= null && (modelViewInv
= this.modelView
.getInverse()) != null)
391 this.eye
= Vec4
.UNIT_W
.transformBy4(modelViewInv
);
396 public Position
getEyePosition()
398 if (this.eyePos
== null)
400 if (this.drawContext
!= null && this.drawContext
.getGlobe() != null)
402 Vec4 eyeVec
= this.getEyePoint();
404 this.eyePos
= this.drawContext
.getGlobe().computePositionFromPoint(eyeVec
);
410 public Vec4
getUpVector()
415 if (this.modelView
!= null && (modelViewInv
= this.modelView
.getInverse()) != null)
416 this.up
= Vec4
.UNIT_Y
.transformBy4(modelViewInv
);
421 public Vec4
getForwardVector()
423 if (this.forward
== null)
426 if (this.modelView
!= null && (modelViewInv
= this.modelView
.getInverse()) != null)
427 this.forward
= Vec4
.UNIT_NEGATIVE_Z
.transformBy4(modelViewInv
);
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)
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)
450 return new Line(a
, b
.subtract3(a
).normalize3());
453 public Position
computePositionFromScreenPoint(double x
, double y
)
455 if (this.drawContext
== null)
458 Globe globe
= this.drawContext
.getGlobe();
462 Line line
= this.computeRayFromScreenPoint(x
, y
);
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)
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();
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();
517 Position globePos
= globe
.computePositionFromPoint(point
);
518 return globePos
.getElevation();
521 protected double computeHeightAboveSurface(DrawContext dc
, Vec4 point
)
523 Globe globe
= dc
.getGlobe();
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();
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
;
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
;
563 // public LatLon computeVisibleLatLonRange()
565 // return null; // TODO
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();
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())
601 if (this.viewStateIterator
.hasNextState(this))
603 this.viewStateIterator
.nextState(this);
604 this.firePropertyChange(AVKey
.VIEW
, null, this);
608 this.stopStateIterators();