diff --git a/CHANGES.md b/CHANGES.md index 127a05c942b5..a660b73f7f9f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,8 @@ Change Log * 3D Tiles geometric error now scales with transform. [#8182](https://github.com/AnalyticalGraphicsInc/cesium/pull/8182) * Fixed per-feature post processing from sometimes selecting the wrong feature. [#7929](https://github.com/AnalyticalGraphicsInc/cesium/pull/7929) * Fixed labels not showing for individual entities in data sources when clustering is enabled. [#6087](https://github.com/AnalyticalGraphicsInc/cesium/issues/6087) +* Fixed a crash for 3D Tiles that have zero volume. [#7945](https://github.com/AnalyticalGraphicsInc/cesium/pull/7945) + ### 1.61 - 2019-09-03 diff --git a/Source/Scene/TileBoundingSphere.js b/Source/Scene/TileBoundingSphere.js index 278e11ad8a51..724fb4f07df7 100644 --- a/Source/Scene/TileBoundingSphere.js +++ b/Source/Scene/TileBoundingSphere.js @@ -7,6 +7,7 @@ define([ '../Core/GeometryInstance', '../Core/Matrix4', '../Core/SphereOutlineGeometry', + '../Core/Math', './PerInstanceColorAppearance', './Primitive' ], function( @@ -18,6 +19,7 @@ define([ GeometryInstance, Matrix4, SphereOutlineGeometry, + CesiumMath, PerInstanceColorAppearance, Primitive) { 'use strict'; @@ -33,6 +35,9 @@ define([ * @private */ function TileBoundingSphere(center, radius) { + if (radius === 0) { + radius = CesiumMath.EPSILON7; + } this._boundingSphere = new BoundingSphere(center, radius); } diff --git a/Source/Scene/TileOrientedBoundingBox.js b/Source/Scene/TileOrientedBoundingBox.js index c1f0ef277694..1bcafc5a9732 100644 --- a/Source/Scene/TileOrientedBoundingBox.js +++ b/Source/Scene/TileOrientedBoundingBox.js @@ -9,6 +9,7 @@ define([ '../Core/Matrix3', '../Core/Matrix4', '../Core/OrientedBoundingBox', + '../Core/Math', './PerInstanceColorAppearance', './Primitive' ], function( @@ -22,10 +23,70 @@ define([ Matrix3, Matrix4, OrientedBoundingBox, + CesiumMath, PerInstanceColorAppearance, Primitive) { 'use strict'; + var scratchU = new Cartesian3(); + var scratchV = new Cartesian3(); + var scratchW = new Cartesian3(); + var scratchCartesian = new Cartesian3(); + + function computeMissingVector(a, b, result) { + result = Cartesian3.cross(a, b, result); + var magnitude = Cartesian3.magnitude(result); + return Cartesian3.multiplyByScalar(result, CesiumMath.EPSILON7 / magnitude, result); + } + + function findOrthogonalVector(a, result) { + var temp = Cartesian3.normalize(a, scratchCartesian); + var b = Cartesian3.equalsEpsilon(temp, Cartesian3.UNIT_X, CesiumMath.EPSILON6) ? Cartesian3.UNIT_Y : Cartesian3.UNIT_X; + return computeMissingVector(a, b, result); + } + + function checkHalfAxes(halfAxes) { + var u = Matrix3.getColumn(halfAxes, 0, scratchU); + var v = Matrix3.getColumn(halfAxes, 1, scratchV); + var w = Matrix3.getColumn(halfAxes, 2, scratchW); + + var uZero = Cartesian3.equals(u, Cartesian3.ZERO); + var vZero = Cartesian3.equals(v, Cartesian3.ZERO); + var wZero = Cartesian3.equals(w, Cartesian3.ZERO); + + if (!uZero && !vZero && !wZero) { + return halfAxes; + } + if (uZero && vZero && wZero) { + halfAxes[0] = CesiumMath.EPSILON7; + halfAxes[4] = CesiumMath.EPSILON7; + halfAxes[8] = CesiumMath.EPSILON7; + return halfAxes; + } + if (uZero && !vZero && !wZero) { + u = computeMissingVector(v, w, u); + } else if (!uZero && vZero && !wZero) { + v = computeMissingVector(u, w, v); + } else if (!uZero && !vZero && wZero) { + w = computeMissingVector(v, u, w); + } else if (!uZero) { + v = findOrthogonalVector(u, v); + w = computeMissingVector(v, u, w); + } else if (!vZero) { + u = findOrthogonalVector(v, u); + w = computeMissingVector(v, u, w); + } else if (!wZero) { + u = findOrthogonalVector(w, u); + v = computeMissingVector(w, u, v); + } + + Matrix3.setColumn(halfAxes, 0, u, halfAxes); + Matrix3.setColumn(halfAxes, 1, v, halfAxes); + Matrix3.setColumn(halfAxes, 2, w, halfAxes); + + return halfAxes; + } + /** * A tile bounding volume specified as an oriented bounding box. * @alias TileOrientedBoundingBox @@ -39,6 +100,7 @@ define([ * @private */ function TileOrientedBoundingBox(center, halfAxes) { + halfAxes = checkHalfAxes(halfAxes); this._orientedBoundingBox = new OrientedBoundingBox(center, halfAxes); this._boundingSphere = BoundingSphere.fromOrientedBoundingBox(this._orientedBoundingBox); } @@ -111,6 +173,7 @@ define([ */ TileOrientedBoundingBox.prototype.update = function(center, halfAxes) { Cartesian3.clone(center, this._orientedBoundingBox.center); + halfAxes = checkHalfAxes(halfAxes); Matrix3.clone(halfAxes, this._orientedBoundingBox.halfAxes); BoundingSphere.fromOrientedBoundingBox(this._orientedBoundingBox, this._boundingSphere); }; @@ -128,8 +191,8 @@ define([ var geometry = new BoxOutlineGeometry({ // Make a 2x2x2 cube - minimum: new Cartesian3(-1.0, -1.0, -1.0), - maximum: new Cartesian3(1.0, 1.0, 1.0) + minimum : new Cartesian3(-1.0, -1.0, -1.0), + maximum : new Cartesian3(1.0, 1.0, 1.0) }); var modelMatrix = Matrix4.fromRotationTranslation(this.boundingVolume.halfAxes, this.boundingVolume.center); var instance = new GeometryInstance({ diff --git a/Specs/Scene/Cesium3DTileSpec.js b/Specs/Scene/Cesium3DTileSpec.js index 3dfb834ccce8..0095f58ac7be 100644 --- a/Specs/Scene/Cesium3DTileSpec.js +++ b/Specs/Scene/Cesium3DTileSpec.js @@ -240,6 +240,33 @@ describe('Scene/Cesium3DTile', function() { expect(tile.boundingVolume).toEqual(obb); }); + it('does not crash for bounding box with 0 volume', function() { + // Create a copy of the tile with bounding box. + var tileWithBoundingBox0Volume = JSON.parse(JSON.stringify(tileWithBoundingBox)); + // Generate all the combinations of missing axes. + var boxes = []; + for (var x = 0; x < 2; ++x) { + for (var y = 0; y < 2; ++y) { + for (var z = 0; z < 2; ++z) { + boxes.push([0.0, 0.0, 0.0, x, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, z]); + } + } + } + + for (var i = 0; i < boxes.length; ++i) { + var box = boxes[i]; + + tileWithBoundingBox0Volume.boundingVolume.box = box; + + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingBox0Volume, undefined); + expect(tile.boundingVolume).toBeDefined(); + var center = new Cartesian3(box[0], box[1], box[2]); + var halfAxes = Matrix3.fromArray(box, 3); + var obb = new TileOrientedBoundingBox(center, halfAxes); + expect(tile.boundingVolume).toEqual(obb); + } + }); + it('can have a content oriented bounding box', function() { var box = tileWithContentBoundingBox.boundingVolume.box; var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithContentBoundingBox, undefined);