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
;
8 import gov
.nasa
.worldwind
.geom
.*;
10 import java
.util
.logging
.Level
;
14 * @version $Id: BasicOrbitView.java 2126 2007-06-21 21:00:42Z dcollins $
16 public class BasicOrbitView
extends AbstractView
18 // Viewing attributes.
19 private Matrix modelView
= Matrix
.IDENTITY
;
20 private Matrix projection
= Matrix
.IDENTITY
;
21 private Frustum frustum
= null;
22 private double collisionRadius
= -1;
23 // Attribute constants.
24 private static final double MIN_NEAR_CLIP_DISTANCE
= 10.0;
26 // ============== Viewing State ======================= //
27 // ============== Viewing State ======================= //
28 // ============== Viewing State ======================= //
30 protected void doApply(DrawContext dc
)
32 // Update viewing state.
33 if (!dc
.isPickingMode())
35 this.updateViewingState(dc
);
36 this.updateAttributes(dc
);
38 // Set current GL viewing state.
39 this.loadModelViewProjection(dc
, this.modelView
, this.projection
);
42 protected void doInitialize(DrawContext dc
)
44 this.initializeAttributes(dc
);
47 public Frustum
getFrustum()
52 private void updateViewingState(DrawContext dc
)
54 // Update the model-view matrix.
55 this.modelView
= this.computeModelViewMatrix(dc
);
57 // Compute current viewing attributes.
58 Vec4 eyeVec
= Vec4
.UNIT_W
.transformBy4(this.modelView
.getInverse());
59 double nearClipDist
= this.computeNearClipDistance(dc
, eyeVec
);
60 double farClipDist
= this.computeFarClipDistance(dc
, eyeVec
);
62 Angle fov
= this.getFieldOfView();
63 java
.awt
.Rectangle viewport
= this.getViewport();
64 // Update the frustum and projection-matrix from a standard perspective projection.
65 this.projection
= Matrix
.fromPerspective(
67 viewport
.width
, viewport
.height
,
68 nearClipDist
, farClipDist
);
69 this.frustum
= Frustum
.fromPerspective(
70 fov
, viewport
.width
, viewport
.height
,
71 nearClipDist
, farClipDist
);
73 // Update the collision radius.
74 if (this.collisionRadius
< 0.0)
75 this.collisionRadius
= this.computeCollisionRadius(MIN_NEAR_CLIP_DISTANCE
);
78 // ============== Attribute Accessors ======================= //
79 // ============== Attribute Accessors ======================= //
80 // ============== Attribute Accessors ======================= //
82 // Geographic coordinate data.
83 private Angle focusLat
= null;
84 private Angle focusLon
= null;
85 private double eyeDist
= -1;
86 private Angle heading
= null;
87 private Angle pitch
= null;
88 private double altitude
= -1;
90 private void initializeAttributes(DrawContext dc
)
92 this.focusLat
= getInitialLatitude(dc
, this.focusLat
);
93 this.focusLon
= getInitialLongitude(dc
, this.focusLon
);
94 this.altitude
= getInitialAltitude(dc
, this.altitude
);
95 this.eyeDist
= this.altitude
;
96 this.heading
= getInitialHeading(dc
, this.heading
);
97 this.pitch
= getInitialPitch(dc
, this.pitch
);
100 private void updateAttributes(DrawContext dc
)
104 public Angle
getLatitude()
106 return this.focusLat
;
109 public void setLatitude(Angle newLatitude
)
111 if (newLatitude
== null)
113 String message
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
114 WorldWind
.logger().log(Level
.FINE
, message
);
115 throw new IllegalArgumentException(message
);
117 this.focusLat
= this.normalizeLatitude(newLatitude
);
120 public Angle
getLongitude()
122 return this.focusLon
;
125 public void setLongitude(Angle newLongitude
)
127 if (newLongitude
== null)
129 String message
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
130 WorldWind
.logger().log(Level
.FINE
, message
);
131 throw new IllegalArgumentException(message
);
133 this.focusLon
= this.normalizeLongitude(newLongitude
);
136 public double getAltitude()
138 return this.altitude
;
141 public void setAltitude(double newAltitude
)
143 throw new UnsupportedOperationException();
146 public void setLatLon(LatLon newLatLon
)
148 if (newLatLon
== null)
150 String message
= WorldWind
.retrieveErrMsg("nullValue.LatLonIsNull");
151 WorldWind
.logger().log(Level
.FINE
, message
);
152 throw new IllegalArgumentException(message
);
154 this.setLatitude(newLatLon
.getLatitude());
155 this.setLongitude(newLatLon
.getLongitude());
158 public void setLatLonAltitude(Position newPosition
)
160 throw new UnsupportedOperationException();
163 public Angle
getHeading()
168 public void setHeading(Angle newHeading
)
170 if (newHeading
== null)
172 String message
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
173 WorldWind
.logger().log(Level
.FINE
, message
);
174 throw new IllegalArgumentException(message
);
176 this.heading
= this.normalizeHeading(newHeading
);
179 public Angle
getPitch()
184 public void setPitch(Angle newPitch
)
186 if (newPitch
== null)
188 String message
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
189 WorldWind
.logger().log(Level
.FINE
, message
);
190 throw new IllegalArgumentException(message
);
192 this.pitch
= this.normalizePitch(newPitch
);
195 public Angle
getRoll()
200 public void setRoll(Angle newRoll
)
204 public double getZoom()
209 public void setZoom(double newZoom
)
211 this.eyeDist
= this.normalizeZoom(newZoom
);
214 // ============== Attribute Normalizing ======================= //
215 // ============== Attribute Normalizing ======================= //
216 // ============== Attribute Normalizing ======================= //
218 public Angle
normalizeLatitude(Angle latitude
)
220 return clamp(latitude
, Angle
.NEG90
, Angle
.POS90
);
223 public Angle
normalizeLongitude(Angle longitude
)
225 return normalize(longitude
, Angle
.NEG180
, Angle
.POS180
);
228 public double normalizeAltitude(double altitude
)
233 public Angle
normalizeHeading(Angle heading
)
237 String message
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
238 WorldWind
.logger().log(Level
.FINE
, message
);
239 throw new IllegalArgumentException(message
);
241 return normalize(heading
, Angle
.ZERO
, Angle
.POS360
);
244 public Angle
normalizePitch(Angle pitch
)
246 return clamp(pitch
, Angle
.ZERO
, Angle
.POS90
);
249 public Angle
normalizeRoll(Angle roll
)
254 public double normalizeZoom(double zoom
)
256 return Math
.max(this.collisionRadius
, zoom
);
259 private static Angle
clamp(Angle value
, Angle min
, Angle max
)
261 if (value
.compareTo(min
) < 0)
263 else if (value
.compareTo(max
) > 0)
268 private static Angle
normalize(Angle value
, Angle min
, Angle max
)
270 if (value
.compareTo(min
) < 0)
271 return value
.add(max
).subtract(min
);
272 else if (value
.compareTo(max
) > 0)
273 return value
.subtract(max
).add(min
);
277 // ============== Viewing Transforms ======================= //
278 // ============== Viewing Transforms ======================= //
279 // ============== Viewing Transforms ======================= //
281 private Matrix
computeModelViewMatrix(DrawContext dc
)
283 Globe globe
= dc
.getGlobe();
287 Vec4 focusPoint
= globe
.computePointFromPosition(this.focusLat
, this.focusLon
, 0);
288 if (focusPoint
== null)
290 String message
= WorldWind
.retrieveErrMsg("BasicOrbitView.NullSurfacePoint");
291 WorldWind
.logger().log(Level
.FINE
, message
);
292 throw new IllegalStateException(message
);
295 Matrix modelView
= lookAt(this.focusLat
, this.focusLon
, focusPoint
.getLength3(),
296 this.eyeDist
, this.heading
, this.pitch
);
297 Vec4 eyeVec
= Vec4
.UNIT_W
.transformBy4(modelView
.getInverse());
298 Position polarEye
= globe
.computePositionFromPoint(eyeVec
);
300 Vec4 surfacePoint
= computeSurfacePoint(dc
, polarEye
.getLatitude(), polarEye
.getLongitude());
301 if (surfacePoint
!= null)
303 double distanceToSurface
= eyeVec
.getLength3() - this.collisionRadius
- surfacePoint
.getLength3();
304 if (distanceToSurface
< 0.0)
306 Vec4 surfaceNormal
= eyeVec
.normalize3();
307 Vec4 newEye
= Vec4
.fromLine3(Vec4
.ZERO
, eyeVec
.getLength3() - distanceToSurface
, surfaceNormal
);
308 Vec4 forward
= eyeVec
.subtract3(focusPoint
);
309 Vec4 newForward
= newEye
.subtract3(focusPoint
);
310 double dot
= forward
.dot3(newForward
) / (forward
.getLength3() * newForward
.getLength3());
311 if (dot
>= -1.0 && dot
<= 1.0)
313 double pitchChange
= Math
.acos(dot
);
314 this.pitch
= clamp(this.pitch
.subtract(Angle
.fromRadians(pitchChange
)), Angle
.ZERO
, Angle
.POS90
);
315 this.eyeDist
= Math
.max(this.collisionRadius
, newForward
.getLength3());
316 modelView
= lookAt(this.focusLat
, this.focusLon
, focusPoint
.getLength3(), this.eyeDist
,
317 this.heading
, this.pitch
);
322 // Compute the current eyeVec altitude above sea level (Globe radius).
323 eyeVec
= Vec4
.UNIT_W
.transformBy4(modelView
.getInverse());
324 polarEye
= globe
.computePositionFromPoint(eyeVec
);
325 this.altitude
= eyeVec
.getLength3() - globe
.getRadiusAt(polarEye
.getLatitude(), polarEye
.getLongitude());
330 private static Matrix
lookAt(Angle focusX
, Angle focusY
, double focusDistance
,
331 double tiltDistance
, Angle tiltZ
, Angle tiltX
)
333 Matrix m
= Matrix
.IDENTITY
;
334 // Translate model away from eye.
335 m
= m
.multiply(Matrix
.fromTranslation(0, 0, -tiltDistance
));
336 // Apply tilt by rotating about X axis at pivot point.
337 m
= m
.multiply(Matrix
.fromRotationX(tiltX
.multiply(-1)));
338 m
= m
.multiply(Matrix
.fromRotationZ(tiltZ
));
339 m
= m
.multiply(Matrix
.fromTranslation(0, 0, -focusDistance
));
340 // Rotate model to lat/lon of eye point.
341 m
= m
.multiply(Matrix
.fromRotationX(focusX
));
342 m
= m
.multiply(Matrix
.fromRotationY(focusY
.multiply(-1)));
346 // ============== Utilities ======================= //
347 // ============== Utilities ======================= //
348 // ============== Utilities ======================= //
350 private double computeNearClipDistance(DrawContext dc
, Vec4 eyeVec
)
352 Angle fov
= this.getFieldOfView();
354 return MIN_NEAR_CLIP_DISTANCE
;
356 // Compute the most distant near clipping plane.
357 double tanHalfFov
= fov
.tanHalfAngle();
359 MIN_NEAR_CLIP_DISTANCE
,
360 this.altitude
/ (2 * Math
.sqrt(2 * tanHalfFov
* tanHalfFov
+ 1)));
363 private double computeFarClipDistance(DrawContext dc
, Vec4 eyeVec
)
365 // Compute the closest allowable far clipping plane distance.
366 return this.computeHorizonDistance(dc
.getGlobe(), dc
.getVerticalExaggeration(), eyeVec
);
369 private double computeCollisionRadius(double nearClipDistance
)
371 final double MIN_COLLISION_RADIUS
= 1;
373 java
.awt
.Rectangle viewport
= this.getViewport();
374 Angle fov
= this.getFieldOfView();
375 if (viewport
== null || fov
== null)
376 return MIN_COLLISION_RADIUS
;
378 if (nearClipDistance
<= 0)
379 nearClipDistance
= MIN_COLLISION_RADIUS
;
381 // Compute the distance between the eye, and any corner on the near clipping rectangle.
382 double tanHalfFov
= fov
.tanHalfAngle();
383 double clipRectX
= nearClipDistance
* tanHalfFov
;
384 double clipRectY
= viewport
.height
* clipRectX
/ (double) viewport
.width
;
385 return 1 + Math
.sqrt(clipRectX
* clipRectX
+ clipRectY
* clipRectY
+ nearClipDistance
* nearClipDistance
);
388 private static Vec4
computeSurfacePoint(DrawContext dc
, Angle lat
, Angle lon
)
390 SectorGeometryList sg
= dc
.getSurfaceGeometry();
393 Vec4 vec
= sg
.getSurfacePoint(lat
, lon
);
398 Globe globe
= dc
.getGlobe();
402 double elevation
= dc
.getVerticalExaggeration() * globe
.getElevation(lat
, lon
);
403 return globe
.computePointFromPosition(lat
, lon
, elevation
);
406 public LatLon
computeVisibleLatLonRange()