import BoundingSphere from "../Core/BoundingSphere.js"; import BoxOutlineGeometry from "../Core/BoxOutlineGeometry.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Check from "../Core/Check.js"; import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js"; import GeometryInstance from "../Core/GeometryInstance.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import CesiumMath from "../Core/Math.js"; import OrientedBoundingBox from "../Core/OrientedBoundingBox.js"; import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js"; import Primitive from "./Primitive.js"; const scratchU = new Cartesian3(); const scratchV = new Cartesian3(); const scratchW = new Cartesian3(); const scratchCartesian = new Cartesian3(); function computeMissingVector(a, b, result) { result = Cartesian3.cross(a, b, result); const magnitude = Cartesian3.magnitude(result); return Cartesian3.multiplyByScalar( result, CesiumMath.EPSILON7 / magnitude, result ); } function findOrthogonalVector(a, result) { const temp = Cartesian3.normalize(a, scratchCartesian); const b = Cartesian3.equalsEpsilon( temp, Cartesian3.UNIT_X, CesiumMath.EPSILON6 ) ? Cartesian3.UNIT_Y : Cartesian3.UNIT_X; return computeMissingVector(a, b, result); } function checkHalfAxes(halfAxes) { let u = Matrix3.getColumn(halfAxes, 0, scratchU); let v = Matrix3.getColumn(halfAxes, 1, scratchV); let w = Matrix3.getColumn(halfAxes, 2, scratchW); const uZero = Cartesian3.equals(u, Cartesian3.ZERO); const vZero = Cartesian3.equals(v, Cartesian3.ZERO); const 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 * @constructor * * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box. * @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box. * Equivalently, the transformation matrix, to rotate and scale a 2x2x2 * cube centered at the origin. * * @private */ function TileOrientedBoundingBox(center, halfAxes) { halfAxes = checkHalfAxes(halfAxes); this._orientedBoundingBox = new OrientedBoundingBox(center, halfAxes); this._boundingSphere = BoundingSphere.fromOrientedBoundingBox( this._orientedBoundingBox ); } Object.defineProperties(TileOrientedBoundingBox.prototype, { /** * The underlying bounding volume. * * @memberof TileOrientedBoundingBox.prototype * * @type {object} * @readonly */ boundingVolume: { get: function () { return this._orientedBoundingBox; }, }, /** * The underlying bounding sphere. * * @memberof TileOrientedBoundingBox.prototype * * @type {BoundingSphere} * @readonly */ boundingSphere: { get: function () { return this._boundingSphere; }, }, }); /** * Computes the distance between this bounding box and the camera attached to frameState. * * @param {FrameState} frameState The frameState to which the camera is attached. * @returns {number} The distance between the camera and the bounding box in meters. Returns 0 if the camera is inside the bounding volume. */ TileOrientedBoundingBox.prototype.distanceToCamera = function (frameState) { //>>includeStart('debug', pragmas.debug); Check.defined("frameState", frameState); //>>includeEnd('debug'); return Math.sqrt( this._orientedBoundingBox.distanceSquaredTo(frameState.camera.positionWC) ); }; /** * Determines which side of a plane this box is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ TileOrientedBoundingBox.prototype.intersectPlane = function (plane) { //>>includeStart('debug', pragmas.debug); Check.defined("plane", plane); //>>includeEnd('debug'); return this._orientedBoundingBox.intersectPlane(plane); }; /** * Update the bounding box after the tile is transformed. * * @param {Cartesian3} center The center of the box. * @param {Matrix3} halfAxes The three orthogonal half-axes of the bounding box. * Equivalently, the transformation matrix, to rotate and scale a 2x2x2 * cube centered at the origin. */ 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 ); }; /** * Creates a debug primitive that shows the outline of the box. * * @param {Color} color The desired color of the primitive's mesh * @return {Primitive} */ TileOrientedBoundingBox.prototype.createDebugVolume = function (color) { //>>includeStart('debug', pragmas.debug); Check.defined("color", color); //>>includeEnd('debug'); const 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), }); const modelMatrix = Matrix4.fromRotationTranslation( this.boundingVolume.halfAxes, this.boundingVolume.center ); const instance = new GeometryInstance({ geometry: geometry, id: "outline", modelMatrix: modelMatrix, attributes: { color: ColorGeometryInstanceAttribute.fromColor(color), }, }); return new Primitive({ geometryInstances: instance, appearance: new PerInstanceColorAppearance({ translucent: false, flat: true, }), asynchronous: false, }); }; export default TileOrientedBoundingBox;