Skip to content

Commit 2c488cc

Browse files
authored
Merge pull request #8397 from AnalyticalGraphicsInc/underEllipsoidCullingFix_TerrainTiles
Fixed terrain tile culling problems when under ellipsoid
2 parents 7dd9bbb + 26e74f7 commit 2c488cc

10 files changed

+248
-77
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Change Log
55

66
##### Fixes :wrench:
77
* Fixed Geocoder auto-complete suggestions when hosted inside Web Components. [#8425](https://github.com/AnalyticalGraphicsInc/cesium/pull/8425)
8+
* Fixed terrain tile culling problems when under ellipsoid. [#8397](https://github.com/AnalyticalGraphicsInc/cesium/pull/8397)
89
* Fixed primitive culling when below the ellipsoid but above terrain. [#8398](https://github.com/AnalyticalGraphicsInc/cesium/pull/8398)
910

1011
### 1.64.0 - 2019-12-02

Source/Core/EllipsoidalOccluder.js

+162-56
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Check from './Check.js';
44
import defaultValue from './defaultValue.js';
55
import defined from './defined.js';
66
import defineProperties from './defineProperties.js';
7+
import Ellipsoid from './Ellipsoid.js';
78
import Rectangle from './Rectangle.js';
89

910
/**
@@ -96,7 +97,7 @@ import Rectangle from './Rectangle.js';
9697
EllipsoidalOccluder.prototype.isPointVisible = function(occludee) {
9798
var ellipsoid = this._ellipsoid;
9899
var occludeeScaledSpacePosition = ellipsoid.transformPositionToScaledSpace(occludee, scratchCartesian);
99-
return this.isScaledSpacePointVisible(occludeeScaledSpacePosition);
100+
return isScaledSpacePointVisible(occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared);
100101
};
101102

102103
/**
@@ -116,16 +117,39 @@ import Rectangle from './Rectangle.js';
116117
* occluder.isScaledSpacePointVisible(scaledSpacePoint); //returns true
117118
*/
118119
EllipsoidalOccluder.prototype.isScaledSpacePointVisible = function(occludeeScaledSpacePosition) {
119-
// See https://cesium.com/blog/2013/04/25/Horizon-culling/
120-
var cv = this._cameraPositionInScaledSpace;
121-
var vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared;
122-
var vt = Cartesian3.subtract(occludeeScaledSpacePosition, cv, scratchCartesian);
123-
var vtDotVc = -Cartesian3.dot(vt, cv);
124-
// If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and
125-
// in this case, set the culling plane to be on V.
126-
var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : (vtDotVc > vhMagnitudeSquared &&
127-
vtDotVc * vtDotVc / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared);
128-
return !isOccluded;
120+
return isScaledSpacePointVisible(occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared);
121+
};
122+
123+
var scratchCameraPositionInScaledSpaceShrunk = new Cartesian3();
124+
125+
/**
126+
* Similar to {@link EllipsoidalOccluder#isScaledSpacePointVisible} except tests against an
127+
* ellipsoid that has been shrunk by the minimum height when the minimum height is below
128+
* the ellipsoid. This is intended to be used with points generated by
129+
* {@link EllipsoidalOccluder#computeHorizonCullingPointPossiblyUnderEllipsoid} or
130+
* {@link EllipsoidalOccluder#computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid}.
131+
*
132+
* @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space of the possibly-shrunk ellipsoid.
133+
* @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
134+
*/
135+
EllipsoidalOccluder.prototype.isScaledSpacePointVisiblePossiblyUnderEllipsoid = function(occludeeScaledSpacePosition, minimumHeight) {
136+
var ellipsoid = this._ellipsoid;
137+
var vhMagnitudeSquared;
138+
var cv;
139+
140+
if (defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight) {
141+
// This code is similar to the cameraPosition setter, but unrolled for performance because it will be called a lot.
142+
cv = scratchCameraPositionInScaledSpaceShrunk;
143+
cv.x = this._cameraPosition.x / (ellipsoid.radii.x + minimumHeight);
144+
cv.y = this._cameraPosition.y / (ellipsoid.radii.y + minimumHeight);
145+
cv.z = this._cameraPosition.z / (ellipsoid.radii.z + minimumHeight);
146+
vhMagnitudeSquared = cv.x * cv.x + cv.y * cv.y + cv.z * cv.z - 1.0;
147+
} else {
148+
cv = this._cameraPositionInScaledSpace;
149+
vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared;
150+
}
151+
152+
return isScaledSpacePointVisible(occludeeScaledSpacePosition, cv, vhMagnitudeSquared);
129153
};
130154

131155
/**
@@ -145,30 +169,32 @@ import Rectangle from './Rectangle.js';
145169
* @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space.
146170
*/
147171
EllipsoidalOccluder.prototype.computeHorizonCullingPoint = function(directionToPoint, positions, result) {
148-
//>>includeStart('debug', pragmas.debug);
149-
Check.typeOf.object('directionToPoint', directionToPoint);
150-
Check.defined('positions', positions);
151-
//>>includeEnd('debug');
152-
153-
if (!defined(result)) {
154-
result = new Cartesian3();
155-
}
156-
157-
var ellipsoid = this._ellipsoid;
158-
var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint);
159-
var resultMagnitude = 0.0;
160-
161-
for (var i = 0, len = positions.length; i < len; ++i) {
162-
var position = positions[i];
163-
var candidateMagnitude = computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint);
164-
resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
165-
}
166-
167-
return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
172+
return computeHorizonCullingPointFromPositions(this._ellipsoid, directionToPoint, positions, result);
168173
};
169174

170-
var positionScratch = new Cartesian3();
175+
var scratchEllipsoidShrunk = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
171176

177+
/**
178+
* Similar to {@link EllipsoidalOccluder#computeHorizonCullingPoint} except computes the culling
179+
* point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below
180+
* the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
181+
* for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
182+
*
183+
* @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
184+
* A reasonable direction to use is the direction from the center of the ellipsoid to
185+
* the center of the bounding sphere computed from the positions. The direction need not
186+
* be normalized.
187+
* @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions
188+
* must be expressed in a reference frame centered at the ellipsoid and aligned with the
189+
* ellipsoid's axes.
190+
* @param {Number} [minimumHeight] The minimum height of all positions. If this value is undefined, all positions are assumed to be above the ellipsoid.
191+
* @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
192+
* @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
193+
*/
194+
EllipsoidalOccluder.prototype.computeHorizonCullingPointPossiblyUnderEllipsoid = function(directionToPoint, positions, minimumHeight, result) {
195+
var possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(this._ellipsoid, minimumHeight, scratchEllipsoidShrunk);
196+
return computeHorizonCullingPointFromPositions(possiblyShrunkEllipsoid, directionToPoint, positions, result);
197+
};
172198
/**
173199
* Computes a point that can be used for horizon culling from a list of positions. If the point is below
174200
* the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point
@@ -188,31 +214,31 @@ import Rectangle from './Rectangle.js';
188214
* @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space.
189215
*/
190216
EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVertices = function(directionToPoint, vertices, stride, center, result) {
191-
//>>includeStart('debug', pragmas.debug);
192-
Check.typeOf.object('directionToPoint', directionToPoint);
193-
Check.defined('vertices', vertices);
194-
Check.typeOf.number('stride', stride);
195-
//>>includeEnd('debug');
196-
197-
if (!defined(result)) {
198-
result = new Cartesian3();
199-
}
200-
201-
center = defaultValue(center, Cartesian3.ZERO);
202-
var ellipsoid = this._ellipsoid;
203-
var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint);
204-
var resultMagnitude = 0.0;
205-
206-
for (var i = 0, len = vertices.length; i < len; i += stride) {
207-
positionScratch.x = vertices[i] + center.x;
208-
positionScratch.y = vertices[i + 1] + center.y;
209-
positionScratch.z = vertices[i + 2] + center.z;
210-
211-
var candidateMagnitude = computeMagnitude(ellipsoid, positionScratch, scaledSpaceDirectionToPoint);
212-
resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
213-
}
217+
return computeHorizonCullingPointFromVertices(this._ellipsoid, directionToPoint, vertices, stride, center, result);
218+
};
214219

215-
return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
220+
/**
221+
* Similar to {@link EllipsoidalOccluder#computeHorizonCullingPointFromVertices} except computes the culling
222+
* point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below
223+
* the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
224+
* for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
225+
*
226+
* @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
227+
* A reasonable direction to use is the direction from the center of the ellipsoid to
228+
* the center of the bounding sphere computed from the positions. The direction need not
229+
* be normalized.
230+
* @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions
231+
* must be expressed in a reference frame centered at the ellipsoid and aligned with the
232+
* ellipsoid's axes.
233+
* @param {Number} [stride=3]
234+
* @param {Cartesian3} [center=Cartesian3.ZERO]
235+
* @param {Number} [minimumHeight] The minimum height of all vertices. If this value is undefined, all vertices are assumed to be above the ellipsoid.
236+
* @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
237+
* @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
238+
*/
239+
EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid = function(directionToPoint, vertices, stride, center, minimumHeight, result) {
240+
var possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(this._ellipsoid, minimumHeight, scratchEllipsoidShrunk);
241+
return computeHorizonCullingPointFromVertices(possiblyShrunkEllipsoid, directionToPoint, vertices, stride, center, result);
216242
};
217243

218244
var subsampleScratch = [];
@@ -246,6 +272,86 @@ import Rectangle from './Rectangle.js';
246272
return this.computeHorizonCullingPoint(bs.center, positions, result);
247273
};
248274

275+
var scratchEllipsoidShrunkRadii = new Cartesian3();
276+
277+
function getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight, result) {
278+
if (defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight) {
279+
var ellipsoidShrunkRadii = Cartesian3.fromElements(
280+
ellipsoid.radii.x + minimumHeight,
281+
ellipsoid.radii.y + minimumHeight,
282+
ellipsoid.radii.z + minimumHeight,
283+
scratchEllipsoidShrunkRadii
284+
);
285+
ellipsoid = Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, result);
286+
}
287+
return ellipsoid;
288+
}
289+
290+
function computeHorizonCullingPointFromPositions(ellipsoid, directionToPoint, positions, result) {
291+
//>>includeStart('debug', pragmas.debug);
292+
Check.typeOf.object('directionToPoint', directionToPoint);
293+
Check.defined('positions', positions);
294+
//>>includeEnd('debug');
295+
296+
if (!defined(result)) {
297+
result = new Cartesian3();
298+
}
299+
300+
var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint);
301+
var resultMagnitude = 0.0;
302+
303+
for (var i = 0, len = positions.length; i < len; ++i) {
304+
var position = positions[i];
305+
var candidateMagnitude = computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint);
306+
resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
307+
}
308+
309+
return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
310+
}
311+
312+
var positionScratch = new Cartesian3();
313+
314+
function computeHorizonCullingPointFromVertices(ellipsoid, directionToPoint, vertices, stride, center, result) {
315+
//>>includeStart('debug', pragmas.debug);
316+
Check.typeOf.object('directionToPoint', directionToPoint);
317+
Check.defined('vertices', vertices);
318+
Check.typeOf.number('stride', stride);
319+
//>>includeEnd('debug');
320+
321+
if (!defined(result)) {
322+
result = new Cartesian3();
323+
}
324+
325+
stride = defaultValue(stride, 3);
326+
center = defaultValue(center, Cartesian3.ZERO);
327+
var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint);
328+
var resultMagnitude = 0.0;
329+
330+
for (var i = 0, len = vertices.length; i < len; i += stride) {
331+
positionScratch.x = vertices[i] + center.x;
332+
positionScratch.y = vertices[i + 1] + center.y;
333+
positionScratch.z = vertices[i + 2] + center.z;
334+
335+
var candidateMagnitude = computeMagnitude(ellipsoid, positionScratch, scaledSpaceDirectionToPoint);
336+
resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
337+
}
338+
339+
return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
340+
}
341+
342+
function isScaledSpacePointVisible(occludeeScaledSpacePosition, cameraPositionInScaledSpace, distanceToLimbInScaledSpaceSquared) {
343+
// See https://cesium.com/blog/2013/04/25/Horizon-culling/
344+
var cv = cameraPositionInScaledSpace;
345+
var vhMagnitudeSquared = distanceToLimbInScaledSpaceSquared;
346+
var vt = Cartesian3.subtract(occludeeScaledSpacePosition, cv, scratchCartesian);
347+
var vtDotVc = -Cartesian3.dot(vt, cv);
348+
// If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and
349+
// in this case, set the culling plane to be on V.
350+
var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : (vtDotVc > vhMagnitudeSquared &&
351+
vtDotVc * vtDotVc / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared);
352+
return !isOccluded;
353+
}
354+
249355
var scaledSpaceScratch = new Cartesian3();
250356
var directionScratch = new Cartesian3();
251357

Source/Core/HeightmapTessellator.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ import WebMercatorProjection from './WebMercatorProjection.js';
396396
var occludeePointInScaledSpace;
397397
if (hasRelativeToCenter) {
398398
var occluder = new EllipsoidalOccluder(ellipsoid);
399-
occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions);
399+
occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(relativeToCenter, positions, minimumHeight);
400400
}
401401

402402
var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter);

Source/Core/QuantizedMeshTerrainData.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ import TerrainMesh from './TerrainMesh.js';
310310
var maximumHeight = result.maximumHeight;
311311
var boundingSphere = defaultValue(BoundingSphere.clone(result.boundingSphere), that._boundingSphere);
312312
var obb = defaultValue(OrientedBoundingBox.clone(result.orientedBoundingBox), that._orientedBoundingBox);
313-
var occlusionPoint = Cartesian3.clone(that._horizonOcclusionPoint);
313+
var occludeePointInScaledSpace = defaultValue(Cartesian3.clone(result.occludeePointInScaledSpace), that._horizonOcclusionPoint);
314314
var stride = result.vertexStride;
315315
var terrainEncoding = TerrainEncoding.clone(result.encoding);
316316

@@ -326,7 +326,7 @@ import TerrainMesh from './TerrainMesh.js';
326326
minimumHeight,
327327
maximumHeight,
328328
boundingSphere,
329-
occlusionPoint,
329+
occludeePointInScaledSpace,
330330
stride,
331331
obb,
332332
terrainEncoding,

Source/Scene/GlobeSurfaceTileProvider.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ import TileSelectionResult from './TileSelectionResult.js';
602602
return intersection;
603603
}
604604

605-
if (occluders.ellipsoid.isScaledSpacePointVisible(occludeePointInScaledSpace)) {
605+
if (occluders.ellipsoid.isScaledSpacePointVisiblePossiblyUnderEllipsoid(occludeePointInScaledSpace, tileBoundingRegion.minimumHeight)) {
606606
return intersection;
607607
}
608608

@@ -805,17 +805,17 @@ import TileSelectionResult from './TileSelectionResult.js';
805805

806806
var cornerPositionsScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
807807

808-
function computeOccludeePoint(tileProvider, center, rectangle, height, result) {
808+
function computeOccludeePoint(tileProvider, center, rectangle, minimumHeight, maximumHeight, result) {
809809
var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
810810
var ellipsoid = ellipsoidalOccluder.ellipsoid;
811811

812812
var cornerPositions = cornerPositionsScratch;
813-
Cartesian3.fromRadians(rectangle.west, rectangle.south, height, ellipsoid, cornerPositions[0]);
814-
Cartesian3.fromRadians(rectangle.east, rectangle.south, height, ellipsoid, cornerPositions[1]);
815-
Cartesian3.fromRadians(rectangle.west, rectangle.north, height, ellipsoid, cornerPositions[2]);
816-
Cartesian3.fromRadians(rectangle.east, rectangle.north, height, ellipsoid, cornerPositions[3]);
813+
Cartesian3.fromRadians(rectangle.west, rectangle.south, maximumHeight, ellipsoid, cornerPositions[0]);
814+
Cartesian3.fromRadians(rectangle.east, rectangle.south, maximumHeight, ellipsoid, cornerPositions[1]);
815+
Cartesian3.fromRadians(rectangle.west, rectangle.north, maximumHeight, ellipsoid, cornerPositions[2]);
816+
Cartesian3.fromRadians(rectangle.east, rectangle.north, maximumHeight, ellipsoid, cornerPositions[3]);
817817

818-
return ellipsoidalOccluder.computeHorizonCullingPoint(center, cornerPositions, result);
818+
return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, cornerPositions, minimumHeight, result);
819819
}
820820

821821
/**
@@ -863,7 +863,7 @@ import TileSelectionResult from './TileSelectionResult.js';
863863
tile.tilingScheme.ellipsoid,
864864
surfaceTile.orientedBoundingBox);
865865

866-
surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace);
866+
surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.minimumHeight, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace);
867867
}
868868
}
869869

0 commit comments

Comments
 (0)