Open
Description
To avoid camera falling to the ground it is required to correct GL near clip distance calculation taking elevation in camera position into account.
First of all, calculate near clip distance taking terrain elevation at camera point into account in WorldWindow:
@Override
protected void computeViewingTransform(Matrix4 projection, Matrix4 modelview) {
double eyeHorizon = this.globe.horizonDistance(this.navigator.getAltitude());
double atmosphereHorizon = this.globe.horizonDistance(160000);
// 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);
// Compute a Cartesian transform matrix from the Navigator.
this.navigator.getAsViewingMatrix(this.globe, modelview);
}
Terrain elevation determination in Globe should be also added:
public double getElevationAtLocation(double latitude, double longitude) {
Sector sector = Sector.fromDegrees(latitude, longitude, 1E-15, 1E-15); // 1E-15 is used because sector can not have 0 with and height
float[] heights = new float[1];
this.getElevationModel().getHeightGrid(sector, 1, 1, heights);
return heights[0];
}
Then limit navigator movements to avoid passing below the ground:
@Override
protected Camera lookAtToCamera(Globe globe, LookAt lookAt, Camera result) {
Camera camera = super.lookAtToCamera(globe, lookAt, result);
// Check if camera altitude is not under the surface
double elevation = wwd.getElevation(camera.latitude, camera.longitude) * wwd.getVerticalExaggeration() + COLLISION_THRESHOLD;
if(elevation > camera.altitude) {
// Set camera altitude above the surface
camera.altitude = elevation;
// Compute new camera point
globe.geographicToCartesian(camera.latitude, camera.longitude, camera.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) {
camera.tilt = Math.toDegrees(Math.acos(dot));
}
}
return camera;
}
private final static double COLLISION_THRESHOLD = 10.0; // 10m above surface
Metadata
Metadata
Assignees
Labels
No labels