| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226 | /* This file is automatically rebuilt by the Cesium build process. */define(['exports', './Transforms-323408fe', './Matrix2-69c32d33', './RuntimeError-c581ca93', './defaultValue-94c3e563', './AttributeCompression-3cfab808', './ComponentDatatype-b1ea011a'], (function (exports, Transforms, Matrix2, RuntimeError, defaultValue, AttributeCompression, ComponentDatatype) { 'use strict';  /**   * Determine whether or not other objects are visible or hidden behind the visible horizon defined by   * an {@link Ellipsoid} and a camera position.  The ellipsoid is assumed to be located at the   * origin of the coordinate system.  This class uses the algorithm described in the   * {@link https://cesium.com/blog/2013/04/25/Horizon-culling/|Horizon Culling} blog post.   *   * @alias EllipsoidalOccluder   *   * @param {Ellipsoid} ellipsoid The ellipsoid to use as an occluder.   * @param {Cartesian3} [cameraPosition] The coordinate of the viewer/camera.  If this parameter is not   *        specified, {@link EllipsoidalOccluder#cameraPosition} must be called before   *        testing visibility.   *   * @constructor   *   * @example   * // Construct an ellipsoidal occluder with radii 1.0, 1.1, and 0.9.   * const cameraPosition = new Cesium.Cartesian3(5.0, 6.0, 7.0);   * const occluderEllipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9);   * const occluder = new Cesium.EllipsoidalOccluder(occluderEllipsoid, cameraPosition);   *   * @private   */  function EllipsoidalOccluder(ellipsoid, cameraPosition) {    //>>includeStart('debug', pragmas.debug);    RuntimeError.Check.typeOf.object("ellipsoid", ellipsoid);    //>>includeEnd('debug');    this._ellipsoid = ellipsoid;    this._cameraPosition = new Matrix2.Cartesian3();    this._cameraPositionInScaledSpace = new Matrix2.Cartesian3();    this._distanceToLimbInScaledSpaceSquared = 0.0;    // cameraPosition fills in the above values    if (defaultValue.defined(cameraPosition)) {      this.cameraPosition = cameraPosition;    }  }  Object.defineProperties(EllipsoidalOccluder.prototype, {    /**     * Gets the occluding ellipsoid.     * @memberof EllipsoidalOccluder.prototype     * @type {Ellipsoid}     */    ellipsoid: {      get: function () {        return this._ellipsoid;      },    },    /**     * Gets or sets the position of the camera.     * @memberof EllipsoidalOccluder.prototype     * @type {Cartesian3}     */    cameraPosition: {      get: function () {        return this._cameraPosition;      },      set: function (cameraPosition) {        // See https://cesium.com/blog/2013/04/25/Horizon-culling/        const ellipsoid = this._ellipsoid;        const cv = ellipsoid.transformPositionToScaledSpace(          cameraPosition,          this._cameraPositionInScaledSpace        );        const vhMagnitudeSquared = Matrix2.Cartesian3.magnitudeSquared(cv) - 1.0;        Matrix2.Cartesian3.clone(cameraPosition, this._cameraPosition);        this._cameraPositionInScaledSpace = cv;        this._distanceToLimbInScaledSpaceSquared = vhMagnitudeSquared;      },    },  });  const scratchCartesian = new Matrix2.Cartesian3();  /**   * Determines whether or not a point, the <code>occludee</code>, is hidden from view by the occluder.   *   * @param {Cartesian3} occludee The point to test for visibility.   * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.   *   * @example   * const cameraPosition = new Cesium.Cartesian3(0, 0, 2.5);   * const ellipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9);   * const occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition);   * const point = new Cesium.Cartesian3(0, -3, -3);   * occluder.isPointVisible(point); //returns true   */  EllipsoidalOccluder.prototype.isPointVisible = function (occludee) {    const ellipsoid = this._ellipsoid;    const occludeeScaledSpacePosition = ellipsoid.transformPositionToScaledSpace(      occludee,      scratchCartesian    );    return isScaledSpacePointVisible(      occludeeScaledSpacePosition,      this._cameraPositionInScaledSpace,      this._distanceToLimbInScaledSpaceSquared    );  };  /**   * Determines whether or not a point expressed in the ellipsoid scaled space, is hidden from view by the   * occluder.  To transform a Cartesian X, Y, Z position in the coordinate system aligned with the ellipsoid   * into the scaled space, call {@link Ellipsoid#transformPositionToScaledSpace}.   *   * @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space.   * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.   *   * @example   * const cameraPosition = new Cesium.Cartesian3(0, 0, 2.5);   * const ellipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9);   * const occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition);   * const point = new Cesium.Cartesian3(0, -3, -3);   * const scaledSpacePoint = ellipsoid.transformPositionToScaledSpace(point);   * occluder.isScaledSpacePointVisible(scaledSpacePoint); //returns true   */  EllipsoidalOccluder.prototype.isScaledSpacePointVisible = function (    occludeeScaledSpacePosition  ) {    return isScaledSpacePointVisible(      occludeeScaledSpacePosition,      this._cameraPositionInScaledSpace,      this._distanceToLimbInScaledSpaceSquared    );  };  const scratchCameraPositionInScaledSpaceShrunk = new Matrix2.Cartesian3();  /**   * 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} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space of the possibly-shrunk ellipsoid.   * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.   */  EllipsoidalOccluder.prototype.isScaledSpacePointVisiblePossiblyUnderEllipsoid = function (    occludeeScaledSpacePosition,    minimumHeight  ) {    const ellipsoid = this._ellipsoid;    let vhMagnitudeSquared;    let cv;    if (      defaultValue.defined(minimumHeight) &&      minimumHeight < 0.0 &&      ellipsoid.minimumRadius > -minimumHeight    ) {      // This code is similar to the cameraPosition setter, but unrolled for performance because it will be called a lot.      cv = scratchCameraPositionInScaledSpaceShrunk;      cv.x = this._cameraPosition.x / (ellipsoid.radii.x + minimumHeight);      cv.y = this._cameraPosition.y / (ellipsoid.radii.y + minimumHeight);      cv.z = this._cameraPosition.z / (ellipsoid.radii.z + minimumHeight);      vhMagnitudeSquared = cv.x * cv.x + cv.y * cv.y + cv.z * cv.z - 1.0;    } else {      cv = this._cameraPositionInScaledSpace;      vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared;    }    return isScaledSpacePointVisible(      occludeeScaledSpacePosition,      cv,      vhMagnitudeSquared    );  };  /**   * 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 {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.computeHorizonCullingPoint = function (    directionToPoint,    positions,    result  ) {    return computeHorizonCullingPointFromPositions(      this._ellipsoid,      directionToPoint,      positions,      result    );  };  const scratchEllipsoidShrunk = Matrix2.Ellipsoid.clone(Matrix2.Ellipsoid.UNIT_SPHERE);  /**   * 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  ) {    const possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(      this._ellipsoid,      minimumHeight,      scratchEllipsoidShrunk    );    return computeHorizonCullingPointFromPositions(      possiblyShrunkEllipsoid,      directionToPoint,      positions,      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  ) {    const possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(      this._ellipsoid,      minimumHeight,      scratchEllipsoidShrunk    );    return computeHorizonCullingPointFromVertices(      possiblyShrunkEllipsoid,      directionToPoint,      vertices,      stride,      center,      result    );  };  const subsampleScratch = [];  /**   * Computes a point that can be used for horizon culling of a rectangle.  If the point is below   * the horizon, the ellipsoid-conforming rectangle is 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 {Rectangle} rectangle The rectangle for which to compute the horizon culling point.   * @param {Ellipsoid} ellipsoid The ellipsoid on which the rectangle is defined.  This may be different from   *                    the ellipsoid used by this instance for occlusion testing.   * @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.computeHorizonCullingPointFromRectangle = function (    rectangle,    ellipsoid,    result  ) {    //>>includeStart('debug', pragmas.debug);    RuntimeError.Check.typeOf.object("rectangle", rectangle);    //>>includeEnd('debug');    const positions = Matrix2.Rectangle.subsample(      rectangle,      ellipsoid,      0.0,      subsampleScratch    );    const bs = Transforms.BoundingSphere.fromPoints(positions);    // If the bounding sphere center is too close to the center of the occluder, it doesn't make    // sense to try to horizon cull it.    if (Matrix2.Cartesian3.magnitude(bs.center) < 0.1 * ellipsoid.minimumRadius) {      return undefined;    }    return this.computeHorizonCullingPoint(bs.center, positions, result);  };  const scratchEllipsoidShrunkRadii = new Matrix2.Cartesian3();  function getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight, result) {    if (      defaultValue.defined(minimumHeight) &&      minimumHeight < 0.0 &&      ellipsoid.minimumRadius > -minimumHeight    ) {      const ellipsoidShrunkRadii = Matrix2.Cartesian3.fromElements(        ellipsoid.radii.x + minimumHeight,        ellipsoid.radii.y + minimumHeight,        ellipsoid.radii.z + minimumHeight,        scratchEllipsoidShrunkRadii      );      ellipsoid = Matrix2.Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, result);    }    return ellipsoid;  }  function computeHorizonCullingPointFromPositions(    ellipsoid,    directionToPoint,    positions,    result  ) {    //>>includeStart('debug', pragmas.debug);    RuntimeError.Check.typeOf.object("directionToPoint", directionToPoint);    RuntimeError.Check.defined("positions", positions);    //>>includeEnd('debug');    if (!defaultValue.defined(result)) {      result = new Matrix2.Cartesian3();    }    const scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(      ellipsoid,      directionToPoint    );    let resultMagnitude = 0.0;    for (let i = 0, len = positions.length; i < len; ++i) {      const position = positions[i];      const candidateMagnitude = computeMagnitude(        ellipsoid,        position,        scaledSpaceDirectionToPoint      );      if (candidateMagnitude < 0.0) {        // all points should face the same direction, but this one doesn't, so return undefined        return undefined;      }      resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);    }    return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);  }  const positionScratch = new Matrix2.Cartesian3();  function computeHorizonCullingPointFromVertices(    ellipsoid,    directionToPoint,    vertices,    stride,    center,    result  ) {    //>>includeStart('debug', pragmas.debug);    RuntimeError.Check.typeOf.object("directionToPoint", directionToPoint);    RuntimeError.Check.defined("vertices", vertices);    RuntimeError.Check.typeOf.number("stride", stride);    //>>includeEnd('debug');    if (!defaultValue.defined(result)) {      result = new Matrix2.Cartesian3();    }    stride = defaultValue.defaultValue(stride, 3);    center = defaultValue.defaultValue(center, Matrix2.Cartesian3.ZERO);    const scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(      ellipsoid,      directionToPoint    );    let resultMagnitude = 0.0;    for (let i = 0, len = vertices.length; i < len; i += stride) {      positionScratch.x = vertices[i] + center.x;      positionScratch.y = vertices[i + 1] + center.y;      positionScratch.z = vertices[i + 2] + center.z;      const candidateMagnitude = computeMagnitude(        ellipsoid,        positionScratch,        scaledSpaceDirectionToPoint      );      if (candidateMagnitude < 0.0) {        // all points should face the same direction, but this one doesn't, so return undefined        return undefined;      }      resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);    }    return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);  }  function isScaledSpacePointVisible(    occludeeScaledSpacePosition,    cameraPositionInScaledSpace,    distanceToLimbInScaledSpaceSquared  ) {    // See https://cesium.com/blog/2013/04/25/Horizon-culling/    const cv = cameraPositionInScaledSpace;    const vhMagnitudeSquared = distanceToLimbInScaledSpaceSquared;    const vt = Matrix2.Cartesian3.subtract(      occludeeScaledSpacePosition,      cv,      scratchCartesian    );    const vtDotVc = -Matrix2.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.    const isOccluded =      vhMagnitudeSquared < 0        ? vtDotVc > 0        : vtDotVc > vhMagnitudeSquared &&          (vtDotVc * vtDotVc) / Matrix2.Cartesian3.magnitudeSquared(vt) >            vhMagnitudeSquared;    return !isOccluded;  }  const scaledSpaceScratch = new Matrix2.Cartesian3();  const directionScratch = new Matrix2.Cartesian3();  function computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint) {    const scaledSpacePosition = ellipsoid.transformPositionToScaledSpace(      position,      scaledSpaceScratch    );    let magnitudeSquared = Matrix2.Cartesian3.magnitudeSquared(scaledSpacePosition);    let magnitude = Math.sqrt(magnitudeSquared);    const direction = Matrix2.Cartesian3.divideByScalar(      scaledSpacePosition,      magnitude,      directionScratch    );    // For the purpose of this computation, points below the ellipsoid are consider to be on it instead.    magnitudeSquared = Math.max(1.0, magnitudeSquared);    magnitude = Math.max(1.0, magnitude);    const cosAlpha = Matrix2.Cartesian3.dot(direction, scaledSpaceDirectionToPoint);    const sinAlpha = Matrix2.Cartesian3.magnitude(      Matrix2.Cartesian3.cross(direction, scaledSpaceDirectionToPoint, direction)    );    const cosBeta = 1.0 / magnitude;    const sinBeta = Math.sqrt(magnitudeSquared - 1.0) * cosBeta;    return 1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta);  }  function magnitudeToPoint(    scaledSpaceDirectionToPoint,    resultMagnitude,    result  ) {    // The horizon culling point is undefined if there were no positions from which to compute it,    // the directionToPoint is pointing opposite all of the positions,  or if we computed NaN or infinity.    if (      resultMagnitude <= 0.0 ||      resultMagnitude === 1.0 / 0.0 ||      resultMagnitude !== resultMagnitude    ) {      return undefined;    }    return Matrix2.Cartesian3.multiplyByScalar(      scaledSpaceDirectionToPoint,      resultMagnitude,      result    );  }  const directionToPointScratch = new Matrix2.Cartesian3();  function computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint) {    if (Matrix2.Cartesian3.equals(directionToPoint, Matrix2.Cartesian3.ZERO)) {      return directionToPoint;    }    ellipsoid.transformPositionToScaledSpace(      directionToPoint,      directionToPointScratch    );    return Matrix2.Cartesian3.normalize(directionToPointScratch, directionToPointScratch);  }  /**   * @private   */  const TerrainExaggeration = {};  /**   * Scales a height relative to an offset.   *   * @param {Number} height The height.   * @param {Number} scale A scalar used to exaggerate the terrain. If the value is 1.0 there will be no effect.   * @param {Number} relativeHeight The height relative to which terrain is exaggerated. If the value is 0.0 terrain will be exaggerated relative to the ellipsoid surface.   */  TerrainExaggeration.getHeight = function (height, scale, relativeHeight) {    return (height - relativeHeight) * scale + relativeHeight;  };  const scratchCartographic = new Matrix2.Cartesian3();  /**   * Scales a position by exaggeration.   */  TerrainExaggeration.getPosition = function (    position,    ellipsoid,    terrainExaggeration,    terrainExaggerationRelativeHeight,    result  ) {    const cartographic = ellipsoid.cartesianToCartographic(      position,      scratchCartographic    );    const newHeight = TerrainExaggeration.getHeight(      cartographic.height,      terrainExaggeration,      terrainExaggerationRelativeHeight    );    return Matrix2.Cartesian3.fromRadians(      cartographic.longitude,      cartographic.latitude,      newHeight,      ellipsoid,      result    );  };  /**   * This enumerated type is used to determine how the vertices of the terrain mesh are compressed.   *   * @enum {Number}   *   * @private   */  const TerrainQuantization = {    /**     * The vertices are not compressed.     *     * @type {Number}     * @constant     */    NONE: 0,    /**     * The vertices are compressed to 12 bits.     *     * @type {Number}     * @constant     */    BITS12: 1,  };  var TerrainQuantization$1 = Object.freeze(TerrainQuantization);  const cartesian3Scratch = new Matrix2.Cartesian3();  const cartesian3DimScratch = new Matrix2.Cartesian3();  const cartesian2Scratch = new Matrix2.Cartesian2();  const matrix4Scratch = new Matrix2.Matrix4();  const matrix4Scratch2 = new Matrix2.Matrix4();  const SHIFT_LEFT_12 = Math.pow(2.0, 12.0);  /**   * Data used to quantize and pack the terrain mesh. The position can be unpacked for picking and all attributes   * are unpacked in the vertex shader.   *   * @alias TerrainEncoding   * @constructor   *   * @param {Cartesian3} center The center point of the vertices.   * @param {AxisAlignedBoundingBox} axisAlignedBoundingBox The bounds of the tile in the east-north-up coordinates at the tiles center.   * @param {Number} minimumHeight The minimum height.   * @param {Number} maximumHeight The maximum height.   * @param {Matrix4} fromENU The east-north-up to fixed frame matrix at the center of the terrain mesh.   * @param {Boolean} hasVertexNormals If the mesh has vertex normals.   * @param {Boolean} [hasWebMercatorT=false] true if the terrain data includes a Web Mercator texture coordinate; otherwise, false.   * @param {Boolean} [hasGeodeticSurfaceNormals=false] true if the terrain data includes geodetic surface normals; otherwise, false.   * @param {Number} [exaggeration=1.0] A scalar used to exaggerate terrain.   * @param {Number} [exaggerationRelativeHeight=0.0] The relative height from which terrain is exaggerated.   *   * @private   */  function TerrainEncoding(    center,    axisAlignedBoundingBox,    minimumHeight,    maximumHeight,    fromENU,    hasVertexNormals,    hasWebMercatorT,    hasGeodeticSurfaceNormals,    exaggeration,    exaggerationRelativeHeight  ) {    let quantization = TerrainQuantization$1.NONE;    let toENU;    let matrix;    if (      defaultValue.defined(axisAlignedBoundingBox) &&      defaultValue.defined(minimumHeight) &&      defaultValue.defined(maximumHeight) &&      defaultValue.defined(fromENU)    ) {      const minimum = axisAlignedBoundingBox.minimum;      const maximum = axisAlignedBoundingBox.maximum;      const dimensions = Matrix2.Cartesian3.subtract(        maximum,        minimum,        cartesian3DimScratch      );      const hDim = maximumHeight - minimumHeight;      const maxDim = Math.max(Matrix2.Cartesian3.maximumComponent(dimensions), hDim);      if (maxDim < SHIFT_LEFT_12 - 1.0) {        quantization = TerrainQuantization$1.BITS12;      } else {        quantization = TerrainQuantization$1.NONE;      }      toENU = Matrix2.Matrix4.inverseTransformation(fromENU, new Matrix2.Matrix4());      const translation = Matrix2.Cartesian3.negate(minimum, cartesian3Scratch);      Matrix2.Matrix4.multiply(        Matrix2.Matrix4.fromTranslation(translation, matrix4Scratch),        toENU,        toENU      );      const scale = cartesian3Scratch;      scale.x = 1.0 / dimensions.x;      scale.y = 1.0 / dimensions.y;      scale.z = 1.0 / dimensions.z;      Matrix2.Matrix4.multiply(Matrix2.Matrix4.fromScale(scale, matrix4Scratch), toENU, toENU);      matrix = Matrix2.Matrix4.clone(fromENU);      Matrix2.Matrix4.setTranslation(matrix, Matrix2.Cartesian3.ZERO, matrix);      fromENU = Matrix2.Matrix4.clone(fromENU, new Matrix2.Matrix4());      const translationMatrix = Matrix2.Matrix4.fromTranslation(minimum, matrix4Scratch);      const scaleMatrix = Matrix2.Matrix4.fromScale(dimensions, matrix4Scratch2);      const st = Matrix2.Matrix4.multiply(translationMatrix, scaleMatrix, matrix4Scratch);      Matrix2.Matrix4.multiply(fromENU, st, fromENU);      Matrix2.Matrix4.multiply(matrix, st, matrix);    }    /**     * How the vertices of the mesh were compressed.     * @type {TerrainQuantization}     */    this.quantization = quantization;    /**     * The minimum height of the tile including the skirts.     * @type {Number}     */    this.minimumHeight = minimumHeight;    /**     * The maximum height of the tile.     * @type {Number}     */    this.maximumHeight = maximumHeight;    /**     * The center of the tile.     * @type {Cartesian3}     */    this.center = Matrix2.Cartesian3.clone(center);    /**     * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales     * it so each component is in the [0, 1] range.     * @type {Matrix4}     */    this.toScaledENU = toENU;    /**     * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame     * @type {Matrix4}     */    this.fromScaledENU = fromENU;    /**     * The matrix used to decompress the terrain vertices in the shader for RTE rendering.     * @type {Matrix4}     */    this.matrix = matrix;    /**     * The terrain mesh contains normals.     * @type {Boolean}     */    this.hasVertexNormals = hasVertexNormals;    /**     * The terrain mesh contains a vertical texture coordinate following the Web Mercator projection.     * @type {Boolean}     */    this.hasWebMercatorT = defaultValue.defaultValue(hasWebMercatorT, false);    /**     * The terrain mesh contains geodetic surface normals, used for terrain exaggeration.     * @type {Boolean}     */    this.hasGeodeticSurfaceNormals = defaultValue.defaultValue(      hasGeodeticSurfaceNormals,      false    );    /**     * A scalar used to exaggerate terrain.     * @type {Number}     */    this.exaggeration = defaultValue.defaultValue(exaggeration, 1.0);    /**     * The relative height from which terrain is exaggerated.     */    this.exaggerationRelativeHeight = defaultValue.defaultValue(      exaggerationRelativeHeight,      0.0    );    /**     * The number of components in each vertex. This value can differ with different quantizations.     * @type {Number}     */    this.stride = 0;    this._offsetGeodeticSurfaceNormal = 0;    this._offsetVertexNormal = 0;    // Calculate the stride and offsets declared above    this._calculateStrideAndOffsets();  }  TerrainEncoding.prototype.encode = function (    vertexBuffer,    bufferIndex,    position,    uv,    height,    normalToPack,    webMercatorT,    geodeticSurfaceNormal  ) {    const u = uv.x;    const v = uv.y;    if (this.quantization === TerrainQuantization$1.BITS12) {      position = Matrix2.Matrix4.multiplyByPoint(        this.toScaledENU,        position,        cartesian3Scratch      );      position.x = ComponentDatatype.CesiumMath.clamp(position.x, 0.0, 1.0);      position.y = ComponentDatatype.CesiumMath.clamp(position.y, 0.0, 1.0);      position.z = ComponentDatatype.CesiumMath.clamp(position.z, 0.0, 1.0);      const hDim = this.maximumHeight - this.minimumHeight;      const h = ComponentDatatype.CesiumMath.clamp((height - this.minimumHeight) / hDim, 0.0, 1.0);      Matrix2.Cartesian2.fromElements(position.x, position.y, cartesian2Scratch);      const compressed0 = AttributeCompression.AttributeCompression.compressTextureCoordinates(        cartesian2Scratch      );      Matrix2.Cartesian2.fromElements(position.z, h, cartesian2Scratch);      const compressed1 = AttributeCompression.AttributeCompression.compressTextureCoordinates(        cartesian2Scratch      );      Matrix2.Cartesian2.fromElements(u, v, cartesian2Scratch);      const compressed2 = AttributeCompression.AttributeCompression.compressTextureCoordinates(        cartesian2Scratch      );      vertexBuffer[bufferIndex++] = compressed0;      vertexBuffer[bufferIndex++] = compressed1;      vertexBuffer[bufferIndex++] = compressed2;      if (this.hasWebMercatorT) {        Matrix2.Cartesian2.fromElements(webMercatorT, 0.0, cartesian2Scratch);        const compressed3 = AttributeCompression.AttributeCompression.compressTextureCoordinates(          cartesian2Scratch        );        vertexBuffer[bufferIndex++] = compressed3;      }    } else {      Matrix2.Cartesian3.subtract(position, this.center, cartesian3Scratch);      vertexBuffer[bufferIndex++] = cartesian3Scratch.x;      vertexBuffer[bufferIndex++] = cartesian3Scratch.y;      vertexBuffer[bufferIndex++] = cartesian3Scratch.z;      vertexBuffer[bufferIndex++] = height;      vertexBuffer[bufferIndex++] = u;      vertexBuffer[bufferIndex++] = v;      if (this.hasWebMercatorT) {        vertexBuffer[bufferIndex++] = webMercatorT;      }    }    if (this.hasVertexNormals) {      vertexBuffer[bufferIndex++] = AttributeCompression.AttributeCompression.octPackFloat(        normalToPack      );    }    if (this.hasGeodeticSurfaceNormals) {      vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.x;      vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.y;      vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.z;    }    return bufferIndex;  };  const scratchPosition = new Matrix2.Cartesian3();  const scratchGeodeticSurfaceNormal = new Matrix2.Cartesian3();  TerrainEncoding.prototype.addGeodeticSurfaceNormals = function (    oldBuffer,    newBuffer,    ellipsoid  ) {    if (this.hasGeodeticSurfaceNormals) {      return;    }    const oldStride = this.stride;    const vertexCount = oldBuffer.length / oldStride;    this.hasGeodeticSurfaceNormals = true;    this._calculateStrideAndOffsets();    const newStride = this.stride;    for (let index = 0; index < vertexCount; index++) {      for (let offset = 0; offset < oldStride; offset++) {        const oldIndex = index * oldStride + offset;        const newIndex = index * newStride + offset;        newBuffer[newIndex] = oldBuffer[oldIndex];      }      const position = this.decodePosition(newBuffer, index, scratchPosition);      const geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(        position,        scratchGeodeticSurfaceNormal      );      const bufferIndex = index * newStride + this._offsetGeodeticSurfaceNormal;      newBuffer[bufferIndex] = geodeticSurfaceNormal.x;      newBuffer[bufferIndex + 1] = geodeticSurfaceNormal.y;      newBuffer[bufferIndex + 2] = geodeticSurfaceNormal.z;    }  };  TerrainEncoding.prototype.removeGeodeticSurfaceNormals = function (    oldBuffer,    newBuffer  ) {    if (!this.hasGeodeticSurfaceNormals) {      return;    }    const oldStride = this.stride;    const vertexCount = oldBuffer.length / oldStride;    this.hasGeodeticSurfaceNormals = false;    this._calculateStrideAndOffsets();    const newStride = this.stride;    for (let index = 0; index < vertexCount; index++) {      for (let offset = 0; offset < newStride; offset++) {        const oldIndex = index * oldStride + offset;        const newIndex = index * newStride + offset;        newBuffer[newIndex] = oldBuffer[oldIndex];      }    }  };  TerrainEncoding.prototype.decodePosition = function (buffer, index, result) {    if (!defaultValue.defined(result)) {      result = new Matrix2.Cartesian3();    }    index *= this.stride;    if (this.quantization === TerrainQuantization$1.BITS12) {      const xy = AttributeCompression.AttributeCompression.decompressTextureCoordinates(        buffer[index],        cartesian2Scratch      );      result.x = xy.x;      result.y = xy.y;      const zh = AttributeCompression.AttributeCompression.decompressTextureCoordinates(        buffer[index + 1],        cartesian2Scratch      );      result.z = zh.x;      return Matrix2.Matrix4.multiplyByPoint(this.fromScaledENU, result, result);    }    result.x = buffer[index];    result.y = buffer[index + 1];    result.z = buffer[index + 2];    return Matrix2.Cartesian3.add(result, this.center, result);  };  TerrainEncoding.prototype.getExaggeratedPosition = function (    buffer,    index,    result  ) {    result = this.decodePosition(buffer, index, result);    const exaggeration = this.exaggeration;    const exaggerationRelativeHeight = this.exaggerationRelativeHeight;    const hasExaggeration = exaggeration !== 1.0;    if (hasExaggeration && this.hasGeodeticSurfaceNormals) {      const geodeticSurfaceNormal = this.decodeGeodeticSurfaceNormal(        buffer,        index,        scratchGeodeticSurfaceNormal      );      const rawHeight = this.decodeHeight(buffer, index);      const heightDifference =        TerrainExaggeration.getHeight(          rawHeight,          exaggeration,          exaggerationRelativeHeight        ) - rawHeight;      // some math is unrolled for better performance      result.x += geodeticSurfaceNormal.x * heightDifference;      result.y += geodeticSurfaceNormal.y * heightDifference;      result.z += geodeticSurfaceNormal.z * heightDifference;    }    return result;  };  TerrainEncoding.prototype.decodeTextureCoordinates = function (    buffer,    index,    result  ) {    if (!defaultValue.defined(result)) {      result = new Matrix2.Cartesian2();    }    index *= this.stride;    if (this.quantization === TerrainQuantization$1.BITS12) {      return AttributeCompression.AttributeCompression.decompressTextureCoordinates(        buffer[index + 2],        result      );    }    return Matrix2.Cartesian2.fromElements(buffer[index + 4], buffer[index + 5], result);  };  TerrainEncoding.prototype.decodeHeight = function (buffer, index) {    index *= this.stride;    if (this.quantization === TerrainQuantization$1.BITS12) {      const zh = AttributeCompression.AttributeCompression.decompressTextureCoordinates(        buffer[index + 1],        cartesian2Scratch      );      return (        zh.y * (this.maximumHeight - this.minimumHeight) + this.minimumHeight      );    }    return buffer[index + 3];  };  TerrainEncoding.prototype.decodeWebMercatorT = function (buffer, index) {    index *= this.stride;    if (this.quantization === TerrainQuantization$1.BITS12) {      return AttributeCompression.AttributeCompression.decompressTextureCoordinates(        buffer[index + 3],        cartesian2Scratch      ).x;    }    return buffer[index + 6];  };  TerrainEncoding.prototype.getOctEncodedNormal = function (    buffer,    index,    result  ) {    index = index * this.stride + this._offsetVertexNormal;    const temp = buffer[index] / 256.0;    const x = Math.floor(temp);    const y = (temp - x) * 256.0;    return Matrix2.Cartesian2.fromElements(x, y, result);  };  TerrainEncoding.prototype.decodeGeodeticSurfaceNormal = function (    buffer,    index,    result  ) {    index = index * this.stride + this._offsetGeodeticSurfaceNormal;    result.x = buffer[index];    result.y = buffer[index + 1];    result.z = buffer[index + 2];    return result;  };  TerrainEncoding.prototype._calculateStrideAndOffsets = function () {    let vertexStride = 0;    switch (this.quantization) {      case TerrainQuantization$1.BITS12:        vertexStride += 3;        break;      default:        vertexStride += 6;    }    if (this.hasWebMercatorT) {      vertexStride += 1;    }    if (this.hasVertexNormals) {      this._offsetVertexNormal = vertexStride;      vertexStride += 1;    }    if (this.hasGeodeticSurfaceNormals) {      this._offsetGeodeticSurfaceNormal = vertexStride;      vertexStride += 3;    }    this.stride = vertexStride;  };  const attributesIndicesNone = {    position3DAndHeight: 0,    textureCoordAndEncodedNormals: 1,    geodeticSurfaceNormal: 2,  };  const attributesIndicesBits12 = {    compressed0: 0,    compressed1: 1,    geodeticSurfaceNormal: 2,  };  TerrainEncoding.prototype.getAttributes = function (buffer) {    const datatype = ComponentDatatype.ComponentDatatype.FLOAT;    const sizeInBytes = ComponentDatatype.ComponentDatatype.getSizeInBytes(datatype);    const strideInBytes = this.stride * sizeInBytes;    let offsetInBytes = 0;    const attributes = [];    function addAttribute(index, componentsPerAttribute) {      attributes.push({        index: index,        vertexBuffer: buffer,        componentDatatype: datatype,        componentsPerAttribute: componentsPerAttribute,        offsetInBytes: offsetInBytes,        strideInBytes: strideInBytes,      });      offsetInBytes += componentsPerAttribute * sizeInBytes;    }    if (this.quantization === TerrainQuantization$1.NONE) {      addAttribute(attributesIndicesNone.position3DAndHeight, 4);      let componentsTexCoordAndNormals = 2;      componentsTexCoordAndNormals += this.hasWebMercatorT ? 1 : 0;      componentsTexCoordAndNormals += this.hasVertexNormals ? 1 : 0;      addAttribute(        attributesIndicesNone.textureCoordAndEncodedNormals,        componentsTexCoordAndNormals      );      if (this.hasGeodeticSurfaceNormals) {        addAttribute(attributesIndicesNone.geodeticSurfaceNormal, 3);      }    } else {      // When there is no webMercatorT or vertex normals, the attribute only needs 3 components: x/y, z/h, u/v.      // WebMercatorT and vertex normals each take up one component, so if only one of them is present the first      // attribute gets a 4th component. If both are present, we need an additional attribute that has 1 component.      const usingAttribute0Component4 =        this.hasWebMercatorT || this.hasVertexNormals;      const usingAttribute1Component1 =        this.hasWebMercatorT && this.hasVertexNormals;      addAttribute(        attributesIndicesBits12.compressed0,        usingAttribute0Component4 ? 4 : 3      );      if (usingAttribute1Component1) {        addAttribute(attributesIndicesBits12.compressed1, 1);      }      if (this.hasGeodeticSurfaceNormals) {        addAttribute(attributesIndicesBits12.geodeticSurfaceNormal, 3);      }    }    return attributes;  };  TerrainEncoding.prototype.getAttributeLocations = function () {    if (this.quantization === TerrainQuantization$1.NONE) {      return attributesIndicesNone;    }    return attributesIndicesBits12;  };  TerrainEncoding.clone = function (encoding, result) {    if (!defaultValue.defined(encoding)) {      return undefined;    }    if (!defaultValue.defined(result)) {      result = new TerrainEncoding();    }    result.quantization = encoding.quantization;    result.minimumHeight = encoding.minimumHeight;    result.maximumHeight = encoding.maximumHeight;    result.center = Matrix2.Cartesian3.clone(encoding.center);    result.toScaledENU = Matrix2.Matrix4.clone(encoding.toScaledENU);    result.fromScaledENU = Matrix2.Matrix4.clone(encoding.fromScaledENU);    result.matrix = Matrix2.Matrix4.clone(encoding.matrix);    result.hasVertexNormals = encoding.hasVertexNormals;    result.hasWebMercatorT = encoding.hasWebMercatorT;    result.hasGeodeticSurfaceNormals = encoding.hasGeodeticSurfaceNormals;    result.exaggeration = encoding.exaggeration;    result.exaggerationRelativeHeight = encoding.exaggerationRelativeHeight;    result._calculateStrideAndOffsets();    return result;  };  exports.EllipsoidalOccluder = EllipsoidalOccluder;  exports.TerrainEncoding = TerrainEncoding;}));
 |