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

Separate out approximate terrain heights function from GroundPrimitive #6617

Merged
merged 2 commits into from
May 22, 2018
Merged
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
203 changes: 203 additions & 0 deletions Source/Core/ApproximateTerrainHeights.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
define([
'./buildModuleUrl',
'./defaultValue',
'./defined',
'./BoundingSphere',
'./Cartesian2',
'./Cartesian3',
'./Cartographic',
'./Check',
'./DeveloperError',
'./Ellipsoid',
'./GeographicTilingScheme',
'./Rectangle',
'./Resource'
], function(
buildModuleUrl,
defaultValue,
defined,
BoundingSphere,
Cartesian2,
Cartesian3,
Cartographic,
Check,
DeveloperError,
Ellipsoid,
GeographicTilingScheme,
Rectangle,
Resource) {
'use strict';

var scratchDiagonalCartesianNE = new Cartesian3();
var scratchDiagonalCartesianSW = new Cartesian3();
var scratchDiagonalCartographic = new Cartographic();
var scratchCenterCartesian = new Cartesian3();
var scratchSurfaceCartesian = new Cartesian3();

var scratchBoundingSphere = new BoundingSphere();
var tilingScheme = new GeographicTilingScheme();
var scratchCorners = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
var scratchTileXY = new Cartesian2();

/**
* A collection of functions for approximating terrain height
* @private
*/
var ApproximateTerrainHeights = {};

/**
* Initializes the minimum and maximum terrain heights
* @return {Promise}
*/
ApproximateTerrainHeights.initialize = function() {
var initPromise = ApproximateTerrainHeights._initPromise;
if (defined(initPromise)) {
return initPromise;
}

ApproximateTerrainHeights._initPromise = Resource.fetchJson(buildModuleUrl('Assets/approximateTerrainHeights.json')).then(function(json) {
ApproximateTerrainHeights._terrainHeights = json;
});

return ApproximateTerrainHeights._initPromise;
};

/**
* Computes the minimum and maximum terrain heights for a given rectangle
* @param {Rectangle} rectangle THe bounding rectangle
* @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
* @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}}
*/
ApproximateTerrainHeights.getApproximateTerrainHeights = function(rectangle, ellipsoid) {
//>>includeStart('debug', pragmas.debug);
Check.defined('rectangle', rectangle);
if (!defined(ApproximateTerrainHeights._terrainHeights)) {
throw new DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function');
}
//>>includeEnd('debug');
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);

var xyLevel = getTileXYLevel(rectangle);

// Get the terrain min/max for that tile
var minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
if (defined(xyLevel)) {
var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
var heights = ApproximateTerrainHeights._terrainHeights[key];
if (defined(heights)) {
minTerrainHeight = heights[0];
maxTerrainHeight = heights[1];
}

// Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
ellipsoid.cartographicToCartesian(Rectangle.northeast(rectangle, scratchDiagonalCartographic),
scratchDiagonalCartesianNE);
ellipsoid.cartographicToCartesian(Rectangle.southwest(rectangle, scratchDiagonalCartographic),
scratchDiagonalCartesianSW);

Cartesian3.subtract(scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian);
Cartesian3.add(scratchDiagonalCartesianNE,
Cartesian3.multiplyByScalar(scratchCenterCartesian, 0.5, scratchCenterCartesian), scratchCenterCartesian);
var surfacePosition = ellipsoid.scaleToGeodeticSurface(scratchCenterCartesian, scratchSurfaceCartesian);
if (defined(surfacePosition)) {
var distance = Cartesian3.distance(scratchCenterCartesian, surfacePosition);
minTerrainHeight = Math.min(minTerrainHeight, -distance);
} else {
minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
}
}

minTerrainHeight = Math.max(ApproximateTerrainHeights._defaultMinTerrainHeight, minTerrainHeight);

return {
minimumTerrainHeight: minTerrainHeight,
maximumTerrainHeight: maxTerrainHeight
};
};

/**
* Computes the bounding sphere based on the tile heights in the rectangle
* @param {Rectangle} rectangle The bounding rectangle
* @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
* @return {BoundingSphere} The result bounding sphere
*/
ApproximateTerrainHeights.getInstanceBoundingSphere = function(rectangle, ellipsoid) {
//>>includeStart('debug', pragmas.debug);
Check.defined('rectangle', rectangle);
if (!defined(ApproximateTerrainHeights._terrainHeights)) {
throw new DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function');
}
//>>includeEnd('debug');
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);

var xyLevel = getTileXYLevel(rectangle);

// Get the terrain max for that tile
var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
if (defined(xyLevel)) {
var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
var heights = ApproximateTerrainHeights._terrainHeights[key];
if (defined(heights)) {
maxTerrainHeight = heights[1];
}
}

var result = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
BoundingSphere.fromRectangle3D(rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere);

return BoundingSphere.union(result, scratchBoundingSphere, result);
};

function getTileXYLevel(rectangle) {
Cartographic.fromRadians(rectangle.east, rectangle.north, 0.0, scratchCorners[0]);
Cartographic.fromRadians(rectangle.west, rectangle.north, 0.0, scratchCorners[1]);
Cartographic.fromRadians(rectangle.east, rectangle.south, 0.0, scratchCorners[2]);
Cartographic.fromRadians(rectangle.west, rectangle.south, 0.0, scratchCorners[3]);

// Determine which tile the bounding rectangle is in
var lastLevelX = 0, lastLevelY = 0;
var currentX = 0, currentY = 0;
var maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel;
var i;
for(i = 0; i <= maxLevel; ++i) {
var failed = false;
for(var j = 0; j < 4; ++j) {
var corner = scratchCorners[j];
tilingScheme.positionToTileXY(corner, i, scratchTileXY);
if (j === 0) {
currentX = scratchTileXY.x;
currentY = scratchTileXY.y;
} else if(currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
failed = true;
break;
}
}

if (failed) {
break;
}

lastLevelX = currentX;
lastLevelY = currentY;
}

if (i === 0) {
return undefined;
}

return {
x : lastLevelX,
y : lastLevelY,
level : (i > maxLevel) ? maxLevel : (i - 1)
};
}

ApproximateTerrainHeights._terrainHeightsMaxLevel = 6;
ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0;
ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0;
ApproximateTerrainHeights._terrainHeights = undefined;
ApproximateTerrainHeights._initPromise = undefined;

return ApproximateTerrainHeights;
});
130 changes: 12 additions & 118 deletions Source/Scene/GroundPrimitive.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
define([
'../Core/ApproximateTerrainHeights',
'../Core/BoundingSphere',
'../Core/buildModuleUrl',
'../Core/Cartesian2',
Expand Down Expand Up @@ -28,6 +29,7 @@ define([
'./SceneMode',
'./ShadowVolumeAppearance'
], function(
ApproximateTerrainHeights,
BoundingSphere,
buildModuleUrl,
Cartesian2,
Expand Down Expand Up @@ -246,8 +248,8 @@ define([
this._maxHeight = undefined;
this._minHeight = undefined;

this._maxTerrainHeight = GroundPrimitive._defaultMaxTerrainHeight;
this._minTerrainHeight = GroundPrimitive._defaultMinTerrainHeight;
this._maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
this._minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;

this._boundingSpheresKeys = [];
this._boundingSpheres = [];
Expand Down Expand Up @@ -408,12 +410,6 @@ define([
*/
GroundPrimitive.isSupported = ClassificationPrimitive.isSupported;

GroundPrimitive._defaultMaxTerrainHeight = 9000.0;
GroundPrimitive._defaultMinTerrainHeight = -100000.0;

GroundPrimitive._terrainHeights = undefined;
GroundPrimitive._terrainHeightsMaxLevel = 6;

function getComputeMaximumHeightFunction(primitive) {
return function(granularity, ellipsoid) {
var r = ellipsoid.maximumRadius;
Expand All @@ -433,9 +429,6 @@ define([
var scratchBVCartesian = new Cartesian3();
var scratchBVCartographic = new Cartographic();
var scratchBVRectangle = new Rectangle();
var tilingScheme = new GeographicTilingScheme();
var scratchCorners = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
var scratchTileXY = new Cartesian2();

function getRectangle(frameState, geometry) {
var ellipsoid = frameState.mapProjection.ellipsoid;
Expand Down Expand Up @@ -482,110 +475,11 @@ define([
return rectangle;
}

var scratchDiagonalCartesianNE = new Cartesian3();
var scratchDiagonalCartesianSW = new Cartesian3();
var scratchDiagonalCartographic = new Cartographic();
var scratchCenterCartesian = new Cartesian3();
var scratchSurfaceCartesian = new Cartesian3();

function getTileXYLevel(rectangle) {
Cartographic.fromRadians(rectangle.east, rectangle.north, 0.0, scratchCorners[0]);
Cartographic.fromRadians(rectangle.west, rectangle.north, 0.0, scratchCorners[1]);
Cartographic.fromRadians(rectangle.east, rectangle.south, 0.0, scratchCorners[2]);
Cartographic.fromRadians(rectangle.west, rectangle.south, 0.0, scratchCorners[3]);

// Determine which tile the bounding rectangle is in
var lastLevelX = 0, lastLevelY = 0;
var currentX = 0, currentY = 0;
var maxLevel = GroundPrimitive._terrainHeightsMaxLevel;
var i;
for(i = 0; i <= maxLevel; ++i) {
var failed = false;
for(var j = 0; j < 4; ++j) {
var corner = scratchCorners[j];
tilingScheme.positionToTileXY(corner, i, scratchTileXY);
if (j === 0) {
currentX = scratchTileXY.x;
currentY = scratchTileXY.y;
} else if(currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
failed = true;
break;
}
}

if (failed) {
break;
}

lastLevelX = currentX;
lastLevelY = currentY;
}

if (i === 0) {
return undefined;
}

return {
x : lastLevelX,
y : lastLevelY,
level : (i > maxLevel) ? maxLevel : (i - 1)
};
}

function setMinMaxTerrainHeights(primitive, rectangle, ellipsoid) {
var xyLevel = getTileXYLevel(rectangle);

// Get the terrain min/max for that tile
var minTerrainHeight = GroundPrimitive._defaultMinTerrainHeight;
var maxTerrainHeight = GroundPrimitive._defaultMaxTerrainHeight;
if (defined(xyLevel)) {
var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
var heights = GroundPrimitive._terrainHeights[key];
if (defined(heights)) {
minTerrainHeight = heights[0];
maxTerrainHeight = heights[1];
}

// Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
ellipsoid.cartographicToCartesian(Rectangle.northeast(rectangle, scratchDiagonalCartographic),
scratchDiagonalCartesianNE);
ellipsoid.cartographicToCartesian(Rectangle.southwest(rectangle, scratchDiagonalCartographic),
scratchDiagonalCartesianSW);

Cartesian3.subtract(scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian);
Cartesian3.add(scratchDiagonalCartesianNE,
Cartesian3.multiplyByScalar(scratchCenterCartesian, 0.5, scratchCenterCartesian), scratchCenterCartesian);
var surfacePosition = ellipsoid.scaleToGeodeticSurface(scratchCenterCartesian, scratchSurfaceCartesian);
if (defined(surfacePosition)) {
var distance = Cartesian3.distance(scratchCenterCartesian, surfacePosition);
minTerrainHeight = Math.min(minTerrainHeight, -distance);
} else {
minTerrainHeight = GroundPrimitive._defaultMinTerrainHeight;
}
}
var result = ApproximateTerrainHeights.getApproximateTerrainHeights(rectangle, ellipsoid);

primitive._minTerrainHeight = Math.max(GroundPrimitive._defaultMinTerrainHeight, minTerrainHeight);
primitive._maxTerrainHeight = maxTerrainHeight;
}

var scratchBoundingSphere = new BoundingSphere();
function getInstanceBoundingSphere(rectangle, ellipsoid) {
var xyLevel = getTileXYLevel(rectangle);

// Get the terrain max for that tile
var maxTerrainHeight = GroundPrimitive._defaultMaxTerrainHeight;
if (defined(xyLevel)) {
var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
var heights = GroundPrimitive._terrainHeights[key];
if (defined(heights)) {
maxTerrainHeight = heights[1];
}
}

var result = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
BoundingSphere.fromRectangle3D(rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere);

return BoundingSphere.union(result, scratchBoundingSphere, result);
primitive._minTerrainHeight = result.minimumTerrainHeight;
primitive._maxTerrainHeight = result.maximumTerrainHeight;
}

function createBoundingVolume(groundPrimitive, frameState, geometry) {
Expand Down Expand Up @@ -731,10 +625,10 @@ define([
return initPromise;
}

GroundPrimitive._initPromise = Resource.fetchJson(buildModuleUrl('Assets/approximateTerrainHeights.json')).then(function(json) {
GroundPrimitive._initialized = true;
GroundPrimitive._terrainHeights = json;
});
GroundPrimitive._initPromise = ApproximateTerrainHeights.initialize()
.then(function() {
GroundPrimitive._initialized = true;
});

return GroundPrimitive._initPromise;
};
Expand Down Expand Up @@ -801,7 +695,7 @@ define([

var id = instance.id;
if (defined(id) && defined(instanceRectangle)) {
var boundingSphere = getInstanceBoundingSphere(instanceRectangle, ellipsoid);
var boundingSphere = ApproximateTerrainHeights.getInstanceBoundingSphere(instanceRectangle, ellipsoid);
this._boundingSpheresKeys.push(id);
this._boundingSpheres.push(boundingSphere);
}
Expand Down
Loading