diff --git a/Source/Core/EllipsoidalOccluder.js b/Source/Core/EllipsoidalOccluder.js index 8a2c1edc6a66..68320b5c2555 100644 --- a/Source/Core/EllipsoidalOccluder.js +++ b/Source/Core/EllipsoidalOccluder.js @@ -4,6 +4,7 @@ import Check from './Check.js'; import defaultValue from './defaultValue.js'; import defined from './defined.js'; import defineProperties from './defineProperties.js'; +import Ellipsoid from './Ellipsoid.js'; import Rectangle from './Rectangle.js'; /** @@ -116,35 +117,39 @@ import Rectangle from './Rectangle.js'; * occluder.isScaledSpacePointVisible(scaledSpacePoint); //returns true */ EllipsoidalOccluder.prototype.isScaledSpacePointVisible = function(occludeeScaledSpacePosition) { - // See https://cesium.com/blog/2013/04/25/Horizon-culling/ - var cv = this._cameraPositionInScaledSpace; - var vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared; - var vt = Cartesian3.subtract(occludeeScaledSpacePosition, cv, scratchCartesian); - var vtDotVc = -Cartesian3.dot(vt, cv); - // If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and - // in this case, set the culling plane to be on V. - var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : (vtDotVc > vhMagnitudeSquared && - vtDotVc * vtDotVc / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared); - return !isOccluded; + return isScaledSpacePointVisible(occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared); }; + var scratchCameraPositionInScaledSpaceShrunk = new Cartesian3(); + /** - * Computes a point that can be used for horizon culling from a list of positions. If the point is below - * the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point - * is expressed in the ellipsoid-scaled space and is suitable for use with - * {@link EllipsoidalOccluder#isScaledSpacePointVisible}. + * Similar to {@link EllipsoidalOccluder#isScaledSpacePointVisible} except tests against an + * ellipsoid that has been shrunk by the minimum height when the minimum height is below + * the ellipsoid. This is intended to be used with points generated by + * {@link EllipsoidalOccluder#computeHorizonCullingPointPossiblyUnderEllipsoid} or + * {@link EllipsoidalOccluder#computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid}. * - * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. - * A reasonable direction to use is the direction from the center of the ellipsoid to - * the center of the bounding sphere computed from the positions. The direction need not - * be normalized. - * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions - * must be expressed in a reference frame centered at the ellipsoid and aligned with the - * ellipsoid's axes. - * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. - * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space. + * @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space of the possibly-shrunk ellipsoid. + * @returns {Boolean} true if the occludee is visible; otherwise false. */ - EllipsoidalOccluder.prototype.computeHorizonCullingPoint = function(directionToPoint, positions, result) { + EllipsoidalOccluder.prototype.isScaledSpacePointVisiblePossiblyUnderEllipsoid = function(occludeeScaledSpacePosition, minimumHeight) { + var ellipsoid = this._ellipsoid; + if (defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight) { + var cameraPositionInScaledSpaceShrunk = Cartesian3.fromElements( + this._cameraPosition.x / (ellipsoid.radii.x + minimumHeight), + this._cameraPosition.y / (ellipsoid.radii.y + minimumHeight), + this._cameraPosition.z / (ellipsoid.radii.z + minimumHeight), + scratchCameraPositionInScaledSpaceShrunk + ); + + var distanceToLimbInScaledSpaceSquaredShrunk = Cartesian3.magnitudeSquared(cameraPositionInScaledSpaceShrunk) - 1.0; + return isScaledSpacePointVisible(occludeeScaledSpacePosition, cameraPositionInScaledSpaceShrunk, distanceToLimbInScaledSpaceSquaredShrunk); + } + + return isScaledSpacePointVisible(occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared); + }; + + function computeHorizonCullingPoint(ellipsoid, directionToPoint, positions, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object('directionToPoint', directionToPoint); Check.defined('positions', positions); @@ -154,7 +159,6 @@ import Rectangle from './Rectangle.js'; result = new Cartesian3(); } - var ellipsoid = this._ellipsoid; var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint); var resultMagnitude = 0.0; @@ -165,9 +169,7 @@ import Rectangle from './Rectangle.js'; } return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result); - }; - - var positionScratch = new Cartesian3(); + } /** * Computes a point that can be used for horizon culling from a list of positions. If the point is below @@ -179,15 +181,41 @@ import Rectangle from './Rectangle.js'; * A reasonable direction to use is the direction from the center of the ellipsoid to * the center of the bounding sphere computed from the positions. The direction need not * be normalized. - * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions - * must be expressed in a reference frame centered at the ellipsoid and aligned with the - * ellipsoid's axes. - * @param {Number} [stride=3] - * @param {Cartesian3} [center=Cartesian3.ZERO] + * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions + * must be expressed in a reference frame centered at the ellipsoid and aligned with the + * ellipsoid's axes. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space. */ - EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVertices = function(directionToPoint, vertices, stride, center, result) { + EllipsoidalOccluder.prototype.computeHorizonCullingPoint = function(directionToPoint, positions, result) { + return computeHorizonCullingPoint(this._ellipsoid, directionToPoint, positions, result); + }; + + /** + * Similar to {@link EllipsoidalOccluder#computeHorizonCullingPoint} except computes the culling + * point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below + * the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable + * for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}. + * + * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. + * A reasonable direction to use is the direction from the center of the ellipsoid to + * the center of the bounding sphere computed from the positions. The direction need not + * be normalized. + * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions + * must be expressed in a reference frame centered at the ellipsoid and aligned with the + * ellipsoid's axes. + * @param {Number} [minimumHeight] The minimum height of all positions. If this value is undefined, all positions are assumed to be above the ellipsoid. + * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. + * @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space. + */ + EllipsoidalOccluder.prototype.computeHorizonCullingPointPossiblyUnderEllipsoid = function(directionToPoint, positions, minimumHeight, result) { + var possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(this._ellipsoid, minimumHeight); + return computeHorizonCullingPoint(possiblyShrunkEllipsoid, directionToPoint, positions, result); + }; + + var positionScratch = new Cartesian3(); + + function computeHorizonCullingPointFromVertices(ellipsoid, directionToPoint, vertices, stride, center, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object('directionToPoint', directionToPoint); Check.defined('vertices', vertices); @@ -198,8 +226,8 @@ import Rectangle from './Rectangle.js'; result = new Cartesian3(); } + stride = defaultValue(stride, 3); center = defaultValue(center, Cartesian3.ZERO); - var ellipsoid = this._ellipsoid; var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint); var resultMagnitude = 0.0; @@ -213,6 +241,52 @@ import Rectangle from './Rectangle.js'; } return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result); + } + + /** + * Computes a point that can be used for horizon culling from a list of positions. If the point is below + * the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point + * is expressed in the ellipsoid-scaled space and is suitable for use with + * {@link EllipsoidalOccluder#isScaledSpacePointVisible}. + * + * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. + * A reasonable direction to use is the direction from the center of the ellipsoid to + * the center of the bounding sphere computed from the positions. The direction need not + * be normalized. + * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions + * must be expressed in a reference frame centered at the ellipsoid and aligned with the + * ellipsoid's axes. + * @param {Number} [stride=3] + * @param {Cartesian3} [center=Cartesian3.ZERO] + * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. + * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space. + */ + EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVertices = function(directionToPoint, vertices, stride, center, result) { + return computeHorizonCullingPointFromVertices(this._ellipsoid, directionToPoint, vertices, stride, center, result); + }; + + /** + * Similar to {@link EllipsoidalOccluder#computeHorizonCullingPointFromVertices} except computes the culling + * point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below + * the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable + * for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}. + * + * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. + * A reasonable direction to use is the direction from the center of the ellipsoid to + * the center of the bounding sphere computed from the positions. The direction need not + * be normalized. + * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions + * must be expressed in a reference frame centered at the ellipsoid and aligned with the + * ellipsoid's axes. + * @param {Number} [stride=3] + * @param {Cartesian3} [center=Cartesian3.ZERO] + * @param {Number} [minimumHeight] The minimum height of all vertices. If this value is undefined, all vertices are assumed to be above the ellipsoid. + * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. + * @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space. + */ + EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid = function(directionToPoint, vertices, stride, center, minimumHeight, result) { + var possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(this._ellipsoid, minimumHeight); + return computeHorizonCullingPointFromVertices(possiblyShrunkEllipsoid, directionToPoint, vertices, stride, center, result); }; var subsampleScratch = []; @@ -246,6 +320,35 @@ import Rectangle from './Rectangle.js'; return this.computeHorizonCullingPoint(bs.center, positions, result); }; + var scratchEllipsoidShrunkRadii = new Cartesian3(); + var scratchEllipsoidShrunk = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); + + function getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight) { + if (defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight) { + var ellipsoidShrunkRadii = Cartesian3.fromElements( + ellipsoid.radii.x + minimumHeight, + ellipsoid.radii.y + minimumHeight, + ellipsoid.radii.z + minimumHeight, + scratchEllipsoidShrunkRadii + ); + ellipsoid = Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, scratchEllipsoidShrunk); + } + return ellipsoid; + } + + function isScaledSpacePointVisible(occludeeScaledSpacePosition, cameraPositionInScaledSpace, distanceToLimbInScaledSpaceSquared) { + // See https://cesium.com/blog/2013/04/25/Horizon-culling/ + var cv = cameraPositionInScaledSpace; + var vhMagnitudeSquared = distanceToLimbInScaledSpaceSquared; + var vt = Cartesian3.subtract(occludeeScaledSpacePosition, cv, scratchCartesian); + var vtDotVc = -Cartesian3.dot(vt, cv); + // If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and + // in this case, set the culling plane to be on V. + var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : (vtDotVc > vhMagnitudeSquared && + vtDotVc * vtDotVc / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared); + return !isOccluded; + } + var scaledSpaceScratch = new Cartesian3(); var directionScratch = new Cartesian3(); diff --git a/Source/Core/HeightmapTessellator.js b/Source/Core/HeightmapTessellator.js index 2f240f46a003..e4f9eef6933c 100644 --- a/Source/Core/HeightmapTessellator.js +++ b/Source/Core/HeightmapTessellator.js @@ -396,7 +396,7 @@ import WebMercatorProjection from './WebMercatorProjection.js'; var occludeePointInScaledSpace; if (hasRelativeToCenter) { var occluder = new EllipsoidalOccluder(ellipsoid); - occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions); + occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(relativeToCenter, positions, minimumHeight); } var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter); diff --git a/Source/Core/QuantizedMeshTerrainData.js b/Source/Core/QuantizedMeshTerrainData.js index 5c0c5ea14b0e..38086459e4d4 100644 --- a/Source/Core/QuantizedMeshTerrainData.js +++ b/Source/Core/QuantizedMeshTerrainData.js @@ -310,7 +310,7 @@ import TerrainMesh from './TerrainMesh.js'; var maximumHeight = result.maximumHeight; var boundingSphere = defaultValue(BoundingSphere.clone(result.boundingSphere), that._boundingSphere); var obb = defaultValue(OrientedBoundingBox.clone(result.orientedBoundingBox), that._orientedBoundingBox); - var occlusionPoint = Cartesian3.clone(that._horizonOcclusionPoint); + var occludeePointInScaledSpace = defaultValue(Cartesian3.clone(result.occludeePointInScaledSpace), that._horizonOcclusionPoint); var stride = result.vertexStride; var terrainEncoding = TerrainEncoding.clone(result.encoding); @@ -326,7 +326,7 @@ import TerrainMesh from './TerrainMesh.js'; minimumHeight, maximumHeight, boundingSphere, - occlusionPoint, + occludeePointInScaledSpace, stride, obb, terrainEncoding, diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 01daa926b967..36595c12a8f9 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -599,7 +599,7 @@ import TileSelectionResult from './TileSelectionResult.js'; return intersection; } - if (occluders.ellipsoid.isScaledSpacePointVisible(occludeePointInScaledSpace)) { + if (occluders.ellipsoid.isScaledSpacePointVisiblePossiblyUnderEllipsoid(occludeePointInScaledSpace, tileBoundingRegion.minimumHeight)) { return intersection; } @@ -802,17 +802,17 @@ import TileSelectionResult from './TileSelectionResult.js'; var cornerPositionsScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()]; - function computeOccludeePoint(tileProvider, center, rectangle, height, result) { + function computeOccludeePoint(tileProvider, center, rectangle, minimumHeight, maximumHeight, result) { var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid; var ellipsoid = ellipsoidalOccluder.ellipsoid; var cornerPositions = cornerPositionsScratch; - Cartesian3.fromRadians(rectangle.west, rectangle.south, height, ellipsoid, cornerPositions[0]); - Cartesian3.fromRadians(rectangle.east, rectangle.south, height, ellipsoid, cornerPositions[1]); - Cartesian3.fromRadians(rectangle.west, rectangle.north, height, ellipsoid, cornerPositions[2]); - Cartesian3.fromRadians(rectangle.east, rectangle.north, height, ellipsoid, cornerPositions[3]); + Cartesian3.fromRadians(rectangle.west, rectangle.south, maximumHeight, ellipsoid, cornerPositions[0]); + Cartesian3.fromRadians(rectangle.east, rectangle.south, maximumHeight, ellipsoid, cornerPositions[1]); + Cartesian3.fromRadians(rectangle.west, rectangle.north, maximumHeight, ellipsoid, cornerPositions[2]); + Cartesian3.fromRadians(rectangle.east, rectangle.north, maximumHeight, ellipsoid, cornerPositions[3]); - return ellipsoidalOccluder.computeHorizonCullingPoint(center, cornerPositions, result); + return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, cornerPositions, minimumHeight, result); } /** @@ -860,7 +860,7 @@ import TileSelectionResult from './TileSelectionResult.js'; tile.tilingScheme.ellipsoid, surfaceTile.orientedBoundingBox); - surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace); + surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.minimumHeight, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace); } } diff --git a/Source/Scene/TerrainFillMesh.js b/Source/Scene/TerrainFillMesh.js index c63f735a2946..9b579c2f8983 100644 --- a/Source/Scene/TerrainFillMesh.js +++ b/Source/Scene/TerrainFillMesh.js @@ -655,7 +655,7 @@ import TileSelectionResult from './TileSelectionResult.js'; minimumHeight, maximumHeight, BoundingSphere.fromOrientedBoundingBox(obb), - computeOccludeePoint(tileProvider, obb.center, rectangle, maximumHeight), + computeOccludeePoint(tileProvider, obb.center, rectangle, minimumHeight, maximumHeight), encoding.getStride(), obb, encoding, @@ -1183,16 +1183,16 @@ import TileSelectionResult from './TileSelectionResult.js'; var cornerPositionsScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()]; - function computeOccludeePoint(tileProvider, center, rectangle, height, result) { + function computeOccludeePoint(tileProvider, center, rectangle, minimumHeight, maximumHeight, result) { var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid; var ellipsoid = ellipsoidalOccluder.ellipsoid; var cornerPositions = cornerPositionsScratch; - Cartesian3.fromRadians(rectangle.west, rectangle.south, height, ellipsoid, cornerPositions[0]); - Cartesian3.fromRadians(rectangle.east, rectangle.south, height, ellipsoid, cornerPositions[1]); - Cartesian3.fromRadians(rectangle.west, rectangle.north, height, ellipsoid, cornerPositions[2]); - Cartesian3.fromRadians(rectangle.east, rectangle.north, height, ellipsoid, cornerPositions[3]); + Cartesian3.fromRadians(rectangle.west, rectangle.south, maximumHeight, ellipsoid, cornerPositions[0]); + Cartesian3.fromRadians(rectangle.east, rectangle.south, maximumHeight, ellipsoid, cornerPositions[1]); + Cartesian3.fromRadians(rectangle.west, rectangle.north, maximumHeight, ellipsoid, cornerPositions[2]); + Cartesian3.fromRadians(rectangle.east, rectangle.north, maximumHeight, ellipsoid, cornerPositions[3]); - return ellipsoidalOccluder.computeHorizonCullingPoint(center, cornerPositions, result); + return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, cornerPositions, minimumHeight, result); } export default TerrainFillMesh; diff --git a/Source/WorkersES6/createVerticesFromGoogleEarthEnterpriseBuffer.js b/Source/WorkersES6/createVerticesFromGoogleEarthEnterpriseBuffer.js index 3781c0279e26..1431da04d195 100644 --- a/Source/WorkersES6/createVerticesFromGoogleEarthEnterpriseBuffer.js +++ b/Source/WorkersES6/createVerticesFromGoogleEarthEnterpriseBuffer.js @@ -372,7 +372,7 @@ import createTaskProcessorWorker from './createTaskProcessorWorker.js'; } var occluder = new EllipsoidalOccluder(ellipsoid); - var occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions); + var occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(relativeToCenter, positions, minHeight); var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter); var encoding = new TerrainEncoding(aaBox, skirtOptions.hMin, maxHeight, fromENU, false, includeWebMercatorT); diff --git a/Source/WorkersES6/createVerticesFromQuantizedTerrainMesh.js b/Source/WorkersES6/createVerticesFromQuantizedTerrainMesh.js index 7719829b4e66..c65af0f91aca 100644 --- a/Source/WorkersES6/createVerticesFromQuantizedTerrainMesh.js +++ b/Source/WorkersES6/createVerticesFromQuantizedTerrainMesh.js @@ -6,6 +6,7 @@ import Cartesian3 from '../Core/Cartesian3.js'; import Cartographic from '../Core/Cartographic.js'; import defined from '../Core/defined.js'; import Ellipsoid from '../Core/Ellipsoid.js'; +import EllipsoidalOccluder from '../Core/EllipsoidalOccluder.js'; import IndexDatatype from '../Core/IndexDatatype.js'; import CesiumMath from '../Core/Math.js'; import Matrix4 from '../Core/Matrix4.js'; @@ -132,11 +133,18 @@ import createTaskProcessorWorker from './createTaskProcessorWorker.js'; var boundingSphere; if (exaggeration !== 1.0) { - // Bounding volumes and horizon culling point need to be recomputed since the tile payload assumes no exaggeration. + // Bounding volumes need to be recomputed since the tile payload assumes no exaggeration. boundingSphere = BoundingSphere.fromPoints(positions); orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, ellipsoid); } + var occludeePointInScaledSpace; + if (exaggeration !== 1.0 || minimumHeight < 0.0) { + // Horizon culling point needs to be recomputed since the tile payload assumes no exaggeration. + var occluder = new EllipsoidalOccluder(ellipsoid); + occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, positions, minimumHeight); + } + var hMin = minimumHeight; hMin = Math.min(hMin, findMinMaxSkirts(parameters.westIndices, parameters.westSkirtHeight, heights, uvs, rectangle, ellipsoid, toENU, minimum, maximum)); hMin = Math.min(hMin, findMinMaxSkirts(parameters.southIndices, parameters.southSkirtHeight, heights, uvs, rectangle, ellipsoid, toENU, minimum, maximum)); @@ -218,6 +226,7 @@ import createTaskProcessorWorker from './createTaskProcessorWorker.js'; maximumHeight : maximumHeight, boundingSphere : boundingSphere, orientedBoundingBox : orientedBoundingBox, + occludeePointInScaledSpace : occludeePointInScaledSpace, encoding : encoding, skirtIndex : parameters.indices.length }; diff --git a/Source/WorkersES6/upsampleQuantizedTerrainMesh.js b/Source/WorkersES6/upsampleQuantizedTerrainMesh.js index 1939127b11e9..8048ce886d8d 100644 --- a/Source/WorkersES6/upsampleQuantizedTerrainMesh.js +++ b/Source/WorkersES6/upsampleQuantizedTerrainMesh.js @@ -269,7 +269,7 @@ import createTaskProcessorWorker from './createTaskProcessorWorker.js'; var orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, ellipsoid, orientedBoundingBoxScratch); var occluder = new EllipsoidalOccluder(ellipsoid); - var horizonOcclusionPoint = occluder.computeHorizonCullingPointFromVertices(boundingSphere.center, cartesianVertices, 3, boundingSphere.center, horizonOcclusionPointScratch); + var horizonOcclusionPoint = occluder.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid(boundingSphere.center, cartesianVertices, 3, boundingSphere.center, minimumHeight, horizonOcclusionPointScratch); var heightRange = maximumHeight - minimumHeight; diff --git a/Specs/Core/EllipsoidalOccluderSpec.js b/Specs/Core/EllipsoidalOccluderSpec.js index 0d677094926e..4c1c2d3d4422 100644 --- a/Specs/Core/EllipsoidalOccluderSpec.js +++ b/Specs/Core/EllipsoidalOccluderSpec.js @@ -39,6 +39,34 @@ describe('Core/EllipsoidalOccluder', function() { expect(occluder.isScaledSpacePointVisible(scaledSpacePoint)).toEqual(true); }); + it('isScaledSpacePointVisiblePossiblyUnderEllipsoid example works as claimed', function() { + // Tests points that are halfway inside a unit sphere: + // 1) on the diagonal + // 2) on the +y-axis + // The camera is on the +z-axis so it will be able to see the diagonal point but not the +y-axis point. + + var cameraPosition = new Cartesian3(0, 0, 1.0); + var ellipsoid = new Ellipsoid(1.0, 1.0, 1.0); + var occluder = new EllipsoidalOccluder(ellipsoid, cameraPosition); + var height = -0.5; + + var direction = Cartesian3.normalize(new Cartesian3(1.0, 1.0, 1.0), new Cartesian3()); + var point = Cartesian3.multiplyByScalar(direction, 0.5, new Cartesian3()); + var scaledSpacePoint = occluder.computeHorizonCullingPoint(point, [point]); + var scaledSpacePointShrunk = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(point, [point], height); + + expect(occluder.isScaledSpacePointVisible(scaledSpacePoint)).toEqual(false); + expect(occluder.isScaledSpacePointVisiblePossiblyUnderEllipsoid(scaledSpacePointShrunk, height)).toEqual(true); + + direction = new Cartesian3(0.0, 1.0, 0.0); + point = Cartesian3.multiplyByScalar(direction, 0.5, new Cartesian3()); + scaledSpacePoint = occluder.computeHorizonCullingPoint(point, [point]); + scaledSpacePointShrunk = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(point, [point], height); + + expect(occluder.isScaledSpacePointVisible(scaledSpacePoint)).toEqual(false); + expect(occluder.isScaledSpacePointVisiblePossiblyUnderEllipsoid(scaledSpacePointShrunk, height)).toEqual(false); + }); + it('reports not visible when point is directly behind ellipsoid', function() { var ellipsoid = Ellipsoid.WGS84; var occluder = new EllipsoidalOccluder(ellipsoid); @@ -178,6 +206,19 @@ describe('Core/EllipsoidalOccluder', function() { expect(foundOneNearZero).toBe(true); }); + + it('computes a point under the ellipsoid with computeHorizonCullingPointPossiblyUnderEllipsoid', function() { + var ellipsoid = new Ellipsoid(12345.0, 4567.0, 8910.0); + var ellipsoidalOccluder = new EllipsoidalOccluder(ellipsoid); + var positions = [new Cartesian3(12344.0, 0.0, 0.0)]; + var directionToPoint = new Cartesian3(1.0, 0.0, 0.0); + + var result = ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(directionToPoint, positions, -1.0); + + expect(result.x).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(result.y).toEqualEpsilon(0.0, CesiumMath.EPSILON14); + expect(result.z).toEqualEpsilon(0.0, CesiumMath.EPSILON14); + }); }); describe('computeHorizonCullingPointFromVertices', function() { @@ -243,6 +284,20 @@ describe('Core/EllipsoidalOccluder', function() { expect(result1.y).toEqualEpsilon(result2.y, CesiumMath.EPSILON14); expect(result1.z).toEqualEpsilon(result2.z, CesiumMath.EPSILON14); }); + + it('computes a point under the ellipsoid with computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid', function() { + var ellipsoid = new Ellipsoid(12345.0, 4567.0, 8910.0); + var ellipsoidalOccluder = new EllipsoidalOccluder(ellipsoid); + var vertices = [12344.0, 0.0, 0.0]; + var directionToPoint = new Cartesian3(1.0, 0.0, 0.0); + var center = Cartesian3.ZERO; + + var result = ellipsoidalOccluder.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid(directionToPoint, vertices, 3, center, -1.0); + + expect(result.x).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(result.y).toEqualEpsilon(0.0, CesiumMath.EPSILON14); + expect(result.z).toEqualEpsilon(0.0, CesiumMath.EPSILON14); + }); }); describe('computeHorizonCullingPointFromRectangle', function() {