Skip to content

Commit db26f0c

Browse files
authored
Merge pull request #8475 from AnalyticalGraphicsInc/obbBigRectangle
Added support for large Rectangles in OrientedBoundingBox.fromRectangle
2 parents f66452f + d81feec commit db26f0c

10 files changed

+196
-93
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Change Log
88
* Fixed terrain tile culling problems when under ellipsoid. [#8397](https://github.com/AnalyticalGraphicsInc/cesium/pull/8397)
99
* Fixed primitive culling when below the ellipsoid but above terrain. [#8398](https://github.com/AnalyticalGraphicsInc/cesium/pull/8398)
1010
* Improved the translucency calculation for the Water material type. [#8455](https://github.com/AnalyticalGraphicsInc/cesium/pull/8455)
11+
* Fixed bounding volume calculation for `GroundPrimitive`. [#4883](https://github.com/AnalyticalGraphicsInc/cesium/issues/4483)
12+
* Fixed `OrientedBoundingBox.fromRectangle` for rectangles with width greater than 180 degrees. [#8475](https://github.com/AnalyticalGraphicsInc/cesium/pull/8475)
1113
* Fixed globe picking so that it returns the closest intersecting triangle instead of the first intersecting triangle. [#8390](https://github.com/AnalyticalGraphicsInc/cesium/pull/8390)
1214

1315
##### Additions :tada:

Source/Core/CesiumTerrainProvider.js

+6-13
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import GeographicTilingScheme from './GeographicTilingScheme.js';
1212
import getStringFromTypedArray from './getStringFromTypedArray.js';
1313
import HeightmapTerrainData from './HeightmapTerrainData.js';
1414
import IndexDatatype from './IndexDatatype.js';
15-
import CesiumMath from './Math.js';
1615
import OrientedBoundingBox from './OrientedBoundingBox.js';
1716
import QuantizedMeshTerrainData from './QuantizedMeshTerrainData.js';
1817
import Request from './Request.js';
@@ -549,19 +548,13 @@ import TileProviderError from './TileProviderError.js';
549548

550549
var skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0;
551550

551+
// The skirt is not included in the OBB computation. If this ever
552+
// causes any rendering artifacts (cracks), they are expected to be
553+
// minor and in the corners of the screen. It's possible that this
554+
// might need to be changed - just change to `minimumHeight - skirtHeight`
555+
// A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`.
552556
var rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level);
553-
var orientedBoundingBox;
554-
if (rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
555-
// Here, rectangle.width < pi/2, and rectangle.height < pi
556-
// (though it would still work with rectangle.width up to pi)
557-
558-
// The skirt is not included in the OBB computation. If this ever
559-
// causes any rendering artifacts (cracks), they are expected to be
560-
// minor and in the corners of the screen. It's possible that this
561-
// might need to be changed - just change to `minimumHeight - skirtHeight`
562-
// A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`.
563-
orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, provider._tilingScheme.ellipsoid);
564-
}
557+
var orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, provider._tilingScheme.ellipsoid);
565558

566559
return new QuantizedMeshTerrainData({
567560
center : center,

Source/Core/HeightmapTessellator.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,7 @@ import WebMercatorProjection from './WebMercatorProjection.js';
387387

388388
var boundingSphere3D = BoundingSphere.fromPoints(positions);
389389
var orientedBoundingBox;
390-
if (defined(rectangle) && rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
391-
// Here, rectangle.width < pi/2, and rectangle.height < pi
392-
// (though it would still work with rectangle.width up to pi)
390+
if (defined(rectangle)) {
393391
orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, ellipsoid);
394392
}
395393

Source/Core/OrientedBoundingBox.js

+111-61
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ import Rectangle from './Rectangle.js';
231231

232232
var scratchOffset = new Cartesian3();
233233
var scratchScale = new Cartesian3();
234-
function fromTangentPlaneExtents(tangentPlane, minimumX, maximumX, minimumY, maximumY, minimumZ, maximumZ, result) {
234+
function fromPlaneExtents(planeOrigin, planeXAxis, planeYAxis, planeZAxis, minimumX, maximumX, minimumY, maximumY, minimumZ, maximumZ, result) {
235235
//>>includeStart('debug', pragmas.debug);
236236
if (!defined(minimumX) ||
237237
!defined(maximumX) ||
@@ -248,9 +248,9 @@ import Rectangle from './Rectangle.js';
248248
}
249249

250250
var halfAxes = result.halfAxes;
251-
Matrix3.setColumn(halfAxes, 0, tangentPlane.xAxis, halfAxes);
252-
Matrix3.setColumn(halfAxes, 1, tangentPlane.yAxis, halfAxes);
253-
Matrix3.setColumn(halfAxes, 2, tangentPlane.zAxis, halfAxes);
251+
Matrix3.setColumn(halfAxes, 0, planeXAxis, halfAxes);
252+
Matrix3.setColumn(halfAxes, 1, planeYAxis, halfAxes);
253+
Matrix3.setColumn(halfAxes, 2, planeZAxis, halfAxes);
254254

255255
var centerOffset = scratchOffset;
256256
centerOffset.x = (minimumX + maximumX) / 2.0;
@@ -264,17 +264,40 @@ import Rectangle from './Rectangle.js';
264264

265265
var center = result.center;
266266
centerOffset = Matrix3.multiplyByVector(halfAxes, centerOffset, centerOffset);
267-
Cartesian3.add(tangentPlane.origin, centerOffset, center);
267+
Cartesian3.add(planeOrigin, centerOffset, center);
268268
Matrix3.multiplyByScale(halfAxes, scale, halfAxes);
269269

270270
return result;
271271
}
272272

273273
var scratchRectangleCenterCartographic = new Cartographic();
274274
var scratchRectangleCenter = new Cartesian3();
275-
var perimeterCartographicScratch = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
276-
var perimeterCartesianScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
277-
var perimeterProjectedScratch = [new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2()];
275+
var scratchPerimeterCartographicNC = new Cartographic();
276+
var scratchPerimeterCartographicNW = new Cartographic();
277+
var scratchPerimeterCartographicCW = new Cartographic();
278+
var scratchPerimeterCartographicSW = new Cartographic();
279+
var scratchPerimeterCartographicSC = new Cartographic();
280+
var scratchPerimeterCartesianNC = new Cartesian3();
281+
var scratchPerimeterCartesianNW = new Cartesian3();
282+
var scratchPerimeterCartesianCW = new Cartesian3();
283+
var scratchPerimeterCartesianSW = new Cartesian3();
284+
var scratchPerimeterCartesianSC = new Cartesian3();
285+
var scratchPerimeterProjectedNC = new Cartesian2();
286+
var scratchPerimeterProjectedNW = new Cartesian2();
287+
var scratchPerimeterProjectedCW = new Cartesian2();
288+
var scratchPerimeterProjectedSW = new Cartesian2();
289+
var scratchPerimeterProjectedSC = new Cartesian2();
290+
291+
var scratchPlaneOrigin = new Cartesian3();
292+
var scratchPlaneNormal = new Cartesian3();
293+
var scratchPlaneXAxis = new Cartesian3();
294+
var scratchHorizonCartesian = new Cartesian3();
295+
var scratchHorizonProjected = new Cartesian2();
296+
var scratchMaxY = new Cartesian3();
297+
var scratchMinY = new Cartesian3();
298+
var scratchZ = new Cartesian3();
299+
var scratchPlane = new Plane(Cartesian3.UNIT_X, 0.0);
300+
278301
/**
279302
* Computes an OrientedBoundingBox that bounds a {@link Rectangle} on the surface of an {@link Ellipsoid}.
280303
* There are no guarantees about the orientation of the bounding box.
@@ -310,59 +333,86 @@ import Rectangle from './Rectangle.js';
310333
maximumHeight = defaultValue(maximumHeight, 0.0);
311334
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
312335

313-
// The bounding box will be aligned with the tangent plane at the center of the rectangle.
314-
var tangentPointCartographic = Rectangle.center(rectangle, scratchRectangleCenterCartographic);
315-
var tangentPoint = ellipsoid.cartographicToCartesian(tangentPointCartographic, scratchRectangleCenter);
316-
var tangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid);
317-
var plane = tangentPlane.plane;
318-
319-
// Corner arrangement:
320-
// N/+y
321-
// [0] [1] [2]
322-
// W/-x [7] [3] E/+x
323-
// [6] [5] [4]
324-
// S/-y
325-
// "C" refers to the central lat/long, which by default aligns with the tangent point (above).
326-
// If the rectangle spans the equator, CW and CE are instead aligned with the equator.
327-
var perimeterNW = perimeterCartographicScratch[0];
328-
var perimeterNC = perimeterCartographicScratch[1];
329-
var perimeterNE = perimeterCartographicScratch[2];
330-
var perimeterCE = perimeterCartographicScratch[3];
331-
var perimeterSE = perimeterCartographicScratch[4];
332-
var perimeterSC = perimeterCartographicScratch[5];
333-
var perimeterSW = perimeterCartographicScratch[6];
334-
var perimeterCW = perimeterCartographicScratch[7];
335-
336-
var lonCenter = tangentPointCartographic.longitude;
337-
var latCenter = (rectangle.south < 0.0 && rectangle.north > 0.0) ? 0.0 : tangentPointCartographic.latitude;
338-
perimeterSW.latitude = perimeterSC.latitude = perimeterSE.latitude = rectangle.south;
339-
perimeterCW.latitude = perimeterCE.latitude = latCenter;
340-
perimeterNW.latitude = perimeterNC.latitude = perimeterNE.latitude = rectangle.north;
341-
perimeterSW.longitude = perimeterCW.longitude = perimeterNW.longitude = rectangle.west;
342-
perimeterSC.longitude = perimeterNC.longitude = lonCenter;
343-
perimeterSE.longitude = perimeterCE.longitude = perimeterNE.longitude = rectangle.east;
344-
345-
// Compute XY extents using the rectangle at maximum height
346-
perimeterNE.height = perimeterNC.height = perimeterNW.height = perimeterCW.height = perimeterSW.height = perimeterSC.height = perimeterSE.height = perimeterCE.height = maximumHeight;
347-
348-
ellipsoid.cartographicArrayToCartesianArray(perimeterCartographicScratch, perimeterCartesianScratch);
349-
tangentPlane.projectPointsToNearestOnPlane(perimeterCartesianScratch, perimeterProjectedScratch);
350-
// See the `perimeterXX` definitions above for what these are
351-
var minX = Math.min(perimeterProjectedScratch[6].x, perimeterProjectedScratch[7].x, perimeterProjectedScratch[0].x);
352-
var maxX = Math.max(perimeterProjectedScratch[2].x, perimeterProjectedScratch[3].x, perimeterProjectedScratch[4].x);
353-
var minY = Math.min(perimeterProjectedScratch[4].y, perimeterProjectedScratch[5].y, perimeterProjectedScratch[6].y);
354-
var maxY = Math.max(perimeterProjectedScratch[0].y, perimeterProjectedScratch[1].y, perimeterProjectedScratch[2].y);
355-
356-
// Compute minimum Z using the rectangle at minimum height
357-
perimeterNE.height = perimeterNW.height = perimeterSE.height = perimeterSW.height = minimumHeight;
358-
ellipsoid.cartographicArrayToCartesianArray(perimeterCartographicScratch, perimeterCartesianScratch);
359-
var minZ = Math.min(Plane.getPointDistance(plane, perimeterCartesianScratch[0]),
360-
Plane.getPointDistance(plane, perimeterCartesianScratch[2]),
361-
Plane.getPointDistance(plane, perimeterCartesianScratch[4]),
362-
Plane.getPointDistance(plane, perimeterCartesianScratch[6]));
363-
var maxZ = maximumHeight; // Since the tangent plane touches the surface at height = 0, this is okay
364-
365-
return fromTangentPlaneExtents(tangentPlane, minX, maxX, minY, maxY, minZ, maxZ, result);
336+
var minX, maxX, minY, maxY, minZ, maxZ, plane;
337+
338+
if (rectangle.width <= CesiumMath.PI) {
339+
// The bounding box will be aligned with the tangent plane at the center of the rectangle.
340+
var tangentPointCartographic = Rectangle.center(rectangle, scratchRectangleCenterCartographic);
341+
var tangentPoint = ellipsoid.cartographicToCartesian(tangentPointCartographic, scratchRectangleCenter);
342+
var tangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid);
343+
plane = tangentPlane.plane;
344+
345+
// If the rectangle spans the equator, CW is instead aligned with the equator (because it sticks out the farthest at the equator).
346+
var lonCenter = tangentPointCartographic.longitude;
347+
var latCenter = (rectangle.south < 0.0 && rectangle.north > 0.0) ? 0.0 : tangentPointCartographic.latitude;
348+
349+
// Compute XY extents using the rectangle at maximum height
350+
var perimeterCartographicNC = Cartographic.fromRadians(lonCenter, rectangle.north, maximumHeight, scratchPerimeterCartographicNC);
351+
var perimeterCartographicNW = Cartographic.fromRadians(rectangle.west, rectangle.north, maximumHeight, scratchPerimeterCartographicNW);
352+
var perimeterCartographicCW = Cartographic.fromRadians(rectangle.west, latCenter, maximumHeight, scratchPerimeterCartographicCW);
353+
var perimeterCartographicSW = Cartographic.fromRadians(rectangle.west, rectangle.south, maximumHeight, scratchPerimeterCartographicSW);
354+
var perimeterCartographicSC = Cartographic.fromRadians(lonCenter, rectangle.south, maximumHeight, scratchPerimeterCartographicSC);
355+
356+
var perimeterCartesianNC = ellipsoid.cartographicToCartesian(perimeterCartographicNC, scratchPerimeterCartesianNC);
357+
var perimeterCartesianNW = ellipsoid.cartographicToCartesian(perimeterCartographicNW, scratchPerimeterCartesianNW);
358+
var perimeterCartesianCW = ellipsoid.cartographicToCartesian(perimeterCartographicCW, scratchPerimeterCartesianCW);
359+
var perimeterCartesianSW = ellipsoid.cartographicToCartesian(perimeterCartographicSW, scratchPerimeterCartesianSW);
360+
var perimeterCartesianSC = ellipsoid.cartographicToCartesian(perimeterCartographicSC, scratchPerimeterCartesianSC);
361+
362+
var perimeterProjectedNC = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianNC, scratchPerimeterProjectedNC);
363+
var perimeterProjectedNW = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianNW, scratchPerimeterProjectedNW);
364+
var perimeterProjectedCW = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianCW, scratchPerimeterProjectedCW);
365+
var perimeterProjectedSW = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianSW, scratchPerimeterProjectedSW);
366+
var perimeterProjectedSC = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianSC, scratchPerimeterProjectedSC);
367+
368+
minX = Math.min(perimeterProjectedNW.x, perimeterProjectedCW.x, perimeterProjectedSW.x);
369+
maxX = -minX; // symmetrical
370+
371+
maxY = Math.max(perimeterProjectedNW.y, perimeterProjectedNC.y);
372+
minY = Math.min(perimeterProjectedSW.y, perimeterProjectedSC.y);
373+
374+
// Compute minimum Z using the rectangle at minimum height, since it will be deeper than the maximum height
375+
perimeterCartographicNW.height = perimeterCartographicSW.height = minimumHeight;
376+
perimeterCartesianNW = ellipsoid.cartographicToCartesian(perimeterCartographicNW, scratchPerimeterCartesianNW);
377+
perimeterCartesianSW = ellipsoid.cartographicToCartesian(perimeterCartographicSW, scratchPerimeterCartesianSW);
378+
379+
minZ = Math.min(Plane.getPointDistance(plane, perimeterCartesianNW), Plane.getPointDistance(plane, perimeterCartesianSW));
380+
maxZ = maximumHeight; // Since the tangent plane touches the surface at height = 0, this is okay
381+
382+
return fromPlaneExtents(tangentPlane.origin, tangentPlane.xAxis, tangentPlane.yAxis, tangentPlane.zAxis, minX, maxX, minY, maxY, minZ, maxZ, result);
383+
}
384+
385+
// Handle the case where rectangle width is greater than PI (wraps around more than half the ellipsoid).
386+
var fullyAboveEquator = rectangle.south > 0.0;
387+
var fullyBelowEquator = rectangle.north < 0.0;
388+
var latitudeNearestToEquator = fullyAboveEquator ? rectangle.south : (fullyBelowEquator ? rectangle.north : 0.0);
389+
var centerLongitude = Rectangle.center(rectangle, scratchRectangleCenterCartographic).longitude;
390+
391+
// Plane is located at the rectangle's center longitude and the rectangle's latitude that is closest to the equator. It rotates around the Z axis.
392+
// This results in a better fit than the obb approach for smaller rectangles, which orients with the rectangle's center normal.
393+
var planeOrigin = Cartesian3.fromRadians(centerLongitude, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchPlaneOrigin);
394+
planeOrigin.z = 0.0; // center the plane on the equator to simpify plane normal calculation
395+
var isPole = Math.abs(planeOrigin.x) < CesiumMath.EPSILON10 && Math.abs(planeOrigin.y) < CesiumMath.EPSILON10;
396+
var planeNormal = !isPole ? Cartesian3.normalize(planeOrigin, scratchPlaneNormal) : Cartesian3.UNIT_X;
397+
var planeYAxis = Cartesian3.UNIT_Z;
398+
var planeXAxis = Cartesian3.cross(planeNormal, planeYAxis, scratchPlaneXAxis);
399+
plane = Plane.fromPointNormal(planeOrigin, planeNormal, scratchPlane);
400+
401+
// Get the horizon point relative to the center. This will be the farthest extent in the plane's X dimension.
402+
var horizonCartesian = Cartesian3.fromRadians(centerLongitude + CesiumMath.PI_OVER_TWO, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchHorizonCartesian);
403+
maxX = Cartesian3.dot(Plane.projectPointOntoPlane(plane, horizonCartesian, scratchHorizonProjected), planeXAxis);
404+
minX = -maxX; // symmetrical
405+
406+
// Get the min and max Y, using the height that will give the largest extent
407+
maxY = Cartesian3.fromRadians(0.0, rectangle.north, fullyBelowEquator ? minimumHeight : maximumHeight, ellipsoid, scratchMaxY).z;
408+
minY = Cartesian3.fromRadians(0.0, rectangle.south, fullyAboveEquator ? minimumHeight : maximumHeight, ellipsoid, scratchMinY).z;
409+
410+
var farZ = Cartesian3.fromRadians(rectangle.east, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchZ);
411+
minZ = Plane.getPointDistance(plane, farZ);
412+
maxZ = 0.0; // plane origin starts at maxZ already
413+
414+
// min and max are local to the plane axes
415+
return fromPlaneExtents(planeOrigin, planeXAxis, planeYAxis, planeNormal, minX, maxX, minY, maxY, minZ, maxZ, result);
366416
};
367417

368418
/**

Source/Scene/GlobeSurfaceTileProvider.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ import TileSelectionResult from './TileSelectionResult.js';
870870
surfaceTile.boundingVolumeSourceTile = heightSource;
871871

872872
var rectangle = tile.rectangle;
873-
if (defined(rectangle) && rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
873+
if (defined(rectangle)) {
874874
surfaceTile.orientedBoundingBox = OrientedBoundingBox.fromRectangle(
875875
tile.rectangle,
876876
tileBoundingRegion.minimumHeight,

0 commit comments

Comments
 (0)