Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix elevation processing issues duning rendering #237

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class GeneralGlobeActivity extends BasicGlobeActivity {
// UI elements
protected TextView latView;
protected TextView lonView;
protected TextView elevView;
protected TextView altView;
protected ImageView crosshairs;
protected ViewGroup overlay;
Expand Down Expand Up @@ -60,6 +61,7 @@ protected void onCreate(Bundle savedInstanceState) {
this.overlay.setVisibility(View.VISIBLE);
this.latView = (TextView) findViewById(R.id.lat_value);
this.lonView = (TextView) findViewById(R.id.lon_value);
this.elevView = (TextView) findViewById(R.id.elev_value);
this.altView = (TextView) findViewById(R.id.alt_value);
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(this.crosshairs, "alpha", 0f).setDuration(1500);
fadeOut.setStartDelay((long) 500);
Expand Down Expand Up @@ -135,6 +137,7 @@ protected void fadeCrosshairs() {
protected void updateOverlayContents(LookAt lookAt, Camera camera) {
latView.setText(formatLatitude(lookAt.latitude));
lonView.setText(formatLongitude(lookAt.longitude));
elevView.setText(formatElevaton(wwd.getGlobe().getElevationAtLocation(lookAt.latitude, lookAt.longitude)));
altView.setText(formatAltitude(camera.altitude));
}

Expand All @@ -147,6 +150,7 @@ protected void updateOverlayColor(@WorldWind.NavigatorAction int eventAction) {
int color = (eventAction == WorldWind.NAVIGATOR_STOPPED) ? 0xA0FFFF00 /*semi-transparent yellow*/ : Color.YELLOW;
latView.setTextColor(color);
lonView.setTextColor(color);
elevView.setTextColor(color);
altView.setTextColor(color);
}

Expand All @@ -160,6 +164,12 @@ protected String formatLongitude(double longitude) {
return String.format("%7.3f°%s", (longitude * sign), (sign >= 0.0 ? "E" : "W"));
}

protected String formatElevaton(double elevation) {
return String.format("Alt: %,.0f %s",
(elevation < 100000 ? elevation : elevation / 1000),
(elevation < 100000 ? "m" : "km"));
}

protected String formatAltitude(double altitude) {
return String.format("Eye: %,.0f %s",
(altitude < 100000 ? altitude : altitude / 1000),
Expand Down
17 changes: 17 additions & 0 deletions worldwind-examples/src/main/res/layout/globe_content.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,23 @@
android:padding="10dp"
android:text="@string/spacer"/>

<TextView
android:id="@+id/elev_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:shadowColor="#000000"
android:shadowRadius="2"
android:shadowDx="0"
android:shadowDy="0"
android:singleLine="true"/>

<TextView
android:id="@+id/spacer3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@string/spacer"/>

<TextView
android:id="@+id/alt_value"
android:layout_width="wrap_content"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ protected void renderTerrainPickedObject(RenderContext rc) {
// picked object ID and the intersection position.
if (rc.pickRay != null && rc.terrain.intersect(rc.pickRay, this.pickPoint)) {
rc.globe.cartesianToGeographic(this.pickPoint.x, this.pickPoint.y, this.pickPoint.z, this.pickPos);
this.pickPos.altitude = 0; // report the actual altitude, which may not lie on the terrain's surface
rc.offerPickedObject(PickedObject.fromTerrain(pickedObjectId, this.pickPos));
}
}
Expand Down
64 changes: 50 additions & 14 deletions worldwind/src/main/java/gov/nasa/worldwind/Navigator.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,29 @@ public class Navigator {

protected double roll;

private Camera scratchCamera = new Camera();
private final Camera scratchCamera = new Camera();

private Matrix4 modelview = new Matrix4();
private final Matrix4 modelview = new Matrix4();

private Matrix4 origin = new Matrix4();
private final Matrix4 origin = new Matrix4();

private Vec3 originPoint = new Vec3();
private final Vec3 originPoint = new Vec3();

private Position originPos = new Position();
private final Position originPos = new Position();

private Line forwardRay = new Line();
private final Line forwardRay = new Line();

public Navigator() {
private final WorldWindow wwd;

private final static double COLLISION_THRESHOLD = 10.0; // 10m above surface

public Navigator(WorldWindow wwd) {
if (wwd == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Navigator", "constructor", "missingWorldWindow"));
}

this.wwd = wwd;
}

public double getLatitude() {
Expand Down Expand Up @@ -193,16 +203,23 @@ public Matrix4 getAsViewingMatrix(Globe globe, Matrix4 result) {

protected LookAt cameraToLookAt(Globe globe, Camera camera, LookAt result) {
this.cameraToViewingMatrix(globe, camera, this.modelview);
this.modelview.extractEyePoint(this.forwardRay.origin);
this.modelview.extractForwardVector(this.forwardRay.direction);

if (!globe.intersect(this.forwardRay, this.originPoint)) {
double horizon = globe.horizonDistance(camera.altitude);
this.forwardRay.pointAt(horizon, this.originPoint);
// Pick terrain located behind the viewport center point
PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
if (terrainPickedObject != null) {
// Use picked terrain position including approximate rendered altitude
this.originPos.set(terrainPickedObject.getTerrainPosition());
globe.geographicToCartesian(this.originPos.latitude, this.originPos.longitude, this.originPos.altitude, this.originPoint);
globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
} else {
// Center is outside the globe - use point on horizon
this.modelview.extractEyePoint(this.forwardRay.origin);
this.modelview.extractForwardVector(this.forwardRay.direction);
this.forwardRay.pointAt(globe.horizonDistance(camera.altitude), this.originPoint);
globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos);
globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
}

globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos);
globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
this.modelview.multiplyByMatrix(this.origin);

result.latitude = this.originPos.latitude;
Expand Down Expand Up @@ -247,6 +264,25 @@ protected Camera lookAtToCamera(Globe globe, LookAt lookAt, Camera result) {
result.tilt = this.modelview.extractTilt();
result.roll = lookAt.roll; // roll passes straight through

// Check if camera altitude is not under the surface
double elevation = globe.getElevationAtLocation(result.latitude, result.longitude) * wwd.getVerticalExaggeration() + COLLISION_THRESHOLD;
if(elevation > result.altitude) {
// Set camera altitude above the surface
result.altitude = elevation;
// Compute new camera point
globe.geographicToCartesian(result.latitude, result.longitude, result.altitude, originPoint);
// Compute look at point
globe.geographicToCartesian(lookAt.latitude, lookAt.longitude, lookAt.altitude, forwardRay.origin);
// Compute normal to globe in look at point
globe.geographicToCartesianNormal(lookAt.latitude, lookAt.longitude, forwardRay.direction);
// Calculate tilt angle between new camera point and look at point
originPoint.subtract(forwardRay.origin).normalize();
double dot = forwardRay.direction.dot(originPoint);
if (dot >= -1 || dot <= 1) {
result.tilt = Math.toDegrees(Math.acos(dot));
}
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public boolean handleMessage(Message msg) {
}
});

protected Handler moveHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
onNavigatorMoved();
return false;
}
});

public NavigatorEventSupport(WorldWindow wwd) {
if (wwd == null) {
throw new IllegalArgumentException(
Expand All @@ -53,6 +61,7 @@ public NavigatorEventSupport(WorldWindow wwd) {
public void reset() {
this.lastModelview = null;
this.stopHandler.removeMessages(0 /*what*/);
this.moveHandler.removeMessages(0 /*what*/);

if (this.lastTouchEvent != null) {
this.lastTouchEvent.recycle();
Expand Down Expand Up @@ -113,10 +122,13 @@ public void onFrameRendered(RenderContext rc) {

if (this.lastModelview == null) { // this is the first frame; copy the frame's modelview
this.lastModelview = new Matrix4(rc.modelview);
// Notify listeners with stopped event on first frame
this.stopHandler.removeMessages(0 /*what*/);
this.stopHandler.sendEmptyMessage(0 /*what*/);
} else if (!this.lastModelview.equals(rc.modelview)) { // the frame's modelview has changed
this.lastModelview.set(rc.modelview);
// Notify the listeners of a navigator moved event.
this.onNavigatorMoved();
this.moveHandler.sendEmptyMessage(0/*what*/);
// Schedule a navigator stopped event after a specified delay in milliseconds.
this.stopHandler.removeMessages(0 /*what*/);
this.stopHandler.sendEmptyMessageDelayed(0 /*what*/, this.stoppedEventDelay);
Expand Down
35 changes: 22 additions & 13 deletions worldwind/src/main/java/gov/nasa/worldwind/WorldWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public class WorldWindow extends GLSurfaceView implements Choreographer.FrameCal

protected double fieldOfView = 45;

protected Navigator navigator = new Navigator();
protected Navigator navigator = new Navigator(this);

protected NavigatorEventSupport navigatorEvents = new NavigatorEventSupport(this);

Expand Down Expand Up @@ -389,6 +389,10 @@ public void setRenderResourceCache(RenderResourceCache cache) {
this.renderResourceCache = cache;
}

public Viewport getViewport() {
return this.viewport;
}

/**
* Determines the WorldWind objects displayed at a screen point. The screen point is interpreted as coordinates in
* Android screen pixels relative to this View.
Expand Down Expand Up @@ -1039,21 +1043,26 @@ protected void computeViewingTransform(Matrix4 projection, Matrix4 modelview) {
double eyeAltitude = this.navigator.getAltitude();
double eyeHorizon = this.globe.horizonDistance(eyeAltitude);
double atmosphereHorizon = this.globe.horizonDistance(160000);
double near = eyeAltitude * 0.5;
double far = eyeHorizon + atmosphereHorizon;

// Computes the near clip distance that provides a minimum resolution at the far clip plane, based on the OpenGL
// context's depth buffer precision.
if (this.depthBits != 0) {
double maxDepthValue = (1 << this.depthBits) - 1;
double farResolution = 10.0;
double nearDistance = far / (maxDepthValue / (1 - farResolution / far) - maxDepthValue + 1);
// Use the computed near distance only when it's less than our default distance.
if (near > nearDistance) {
near = nearDistance;
}
// The far distance is set to the smallest value that does not clip the atmosphere.
double far = eyeHorizon + atmosphereHorizon;
if (far < 1e3) far = 1e3;

//The near distance is set to a large value that does not clip the globe's surface.
double maxDepthValue = (1L << this.depthBits) - 1L;
double farResolution = 10.0;
double near = far / (maxDepthValue / (1 - farResolution / far) - maxDepthValue + 1);

// Prevent the near clip plane from intersecting the terrain.
double distanceToSurface = this.navigator.getAltitude() - this.globe.getElevationAtLocation(this.navigator.getLatitude(), this.navigator.getLongitude()) * this.getVerticalExaggeration();
if (distanceToSurface > 0) {
double tanHalfFov = Math.tan(0.5 * Math.toRadians(this.fieldOfView));
double maxNearDistance = distanceToSurface / (2 * Math.sqrt(2 * tanHalfFov * tanHalfFov + 1));
if (near > maxNearDistance) near = maxNearDistance;
}

if (near < 1) near = 1;

// Compute a perspective projection matrix given the WorldWindow's viewport, field of view, and clip distances.
projection.setToPerspectiveProjection(this.viewport.width, this.viewport.height, this.fieldOfView, near, far);

Expand Down
19 changes: 19 additions & 0 deletions worldwind/src/main/java/gov/nasa/worldwind/globe/Globe.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public class Globe {
*/
protected GeographicProjection projection;

private final float[] scratchHeights = new float[1];

private final Sector scratchSector = new Sector();

/**
* Constructs a globe with a specified reference ellipsoid and projection.
*
Expand Down Expand Up @@ -320,4 +324,19 @@ public boolean intersect(Line line, Vec3 result) {

return this.projection.intersect(this, line, result);
}

/**
* Determine terrain altitude in specified geographic point from elevation model
*
* @param latitude location latitude
* @param longitude location longitude
*
* @return Elevation in meters in specified location
*/
public double getElevationAtLocation(double latitude, double longitude) {
// Use 1E-15 below because sector can not have zero deltas
this.scratchSector.set(latitude, longitude, 1E-15, 1E-15);
this.getElevationModel().getHeightGrid(this.scratchSector, 1, 1, this.scratchHeights);
return this.scratchHeights[0];
}
}
Loading