| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 | import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian3 from "../Core/Cartesian3.js";import CesiumMath from "../Core/Math.js";import Check from "../Core/Check.js";import Matrix3 from "../Core/Matrix3.js";import Matrix4 from "../Core/Matrix4.js";import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";import defaultValue from "../Core/defaultValue.js";/** * A box {@link VoxelShape}. * * @alias VoxelBoxShape * @constructor * * @see VoxelShape * @see VoxelEllipsoidShape * @see VoxelCylinderShape * @see VoxelShapeType * * @private */function VoxelBoxShape() {  /**   * An oriented bounding box containing the bounded shape.   * The update function must be called before accessing this value.   * @type {OrientedBoundingBox}   * @readonly   */  this.orientedBoundingBox = new OrientedBoundingBox();  /**   * A bounding sphere containing the bounded shape.   * The update function must be called before accessing this value.   * @type {BoundingSphere}   * @readonly   */  this.boundingSphere = new BoundingSphere();  /**   * A transformation matrix containing the bounded shape.   * The update function must be called before accessing this value.   * @type {Matrix4}   * @readonly   */  this.boundTransform = new Matrix4();  /**   * A transformation matrix containing the shape, ignoring the bounds.   * The update function must be called before accessing this value.   * @type {Matrix4}   * @readonly   */  this.shapeTransform = new Matrix4();  /**   * @type {Cartesian3}   * @private   */  this._minBounds = Cartesian3.clone(    VoxelBoxShape.DefaultMinBounds,    new Cartesian3()  );  /**   * @type {Cartesian3}   * @private   */  this._maxBounds = Cartesian3.clone(    VoxelBoxShape.DefaultMaxBounds,    new Cartesian3()  );  /**   * @type {Object<string, any>}   * @readonly   */  this.shaderUniforms = {    renderMinBounds: new Cartesian3(),    renderMaxBounds: new Cartesian3(),    boxUvToShapeUvScale: new Cartesian3(),    boxUvToShapeUvTranslate: new Cartesian3(),  };  /**   * @type {Object<string, any>}   * @readonly   */  this.shaderDefines = {    BOX_INTERSECTION_INDEX: undefined,    BOX_HAS_SHAPE_BOUNDS: undefined,  };  /**   * The maximum number of intersections against the shape for any ray direction.   * @type {number}   * @readonly   */  this.shaderMaximumIntersectionsLength = 0; // not known until update}const scratchCenter = new Cartesian3();const scratchScale = new Cartesian3();const scratchRotation = new Matrix3();const scratchClipMinBounds = new Cartesian3();const scratchClipMaxBounds = new Cartesian3();const scratchRenderMinBounds = new Cartesian3();const scratchRenderMaxBounds = new Cartesian3();const transformLocalToUv = Matrix4.fromRotationTranslation(  Matrix3.fromUniformScale(0.5, new Matrix3()),  new Cartesian3(0.5, 0.5, 0.5),  new Matrix4());/** * Update the shape's state. * * @param {Matrix4} modelMatrix The model matrix. * @param {Cartesian3} minBounds The minimum bounds. * @param {Cartesian3} maxBounds The maximum bounds. * @param {Cartesian3} [clipMinBounds=VoxelBoxShape.DefaultMinBounds] The minimum clip bounds. * @param {Cartesian3} [clipMaxBounds=VoxelBoxShape.DefaultMaxBounds] The maximum clip bounds. * @returns {boolean} Whether the shape is visible. */VoxelBoxShape.prototype.update = function (  modelMatrix,  minBounds,  maxBounds,  clipMinBounds,  clipMaxBounds) {  clipMinBounds = defaultValue(clipMinBounds, VoxelBoxShape.DefaultMinBounds);  clipMaxBounds = defaultValue(clipMaxBounds, VoxelBoxShape.DefaultMaxBounds);  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("modelMatrix", modelMatrix);  Check.typeOf.object("minBounds", minBounds);  Check.typeOf.object("maxBounds", maxBounds);  //>>includeEnd('debug');  const defaultMinBounds = VoxelBoxShape.DefaultMinBounds;  const defaultMaxBounds = VoxelBoxShape.DefaultMaxBounds;  minBounds = this._minBounds = Cartesian3.clamp(    minBounds,    defaultMinBounds,    defaultMaxBounds,    this._minBounds  );  maxBounds = this._maxBounds = Cartesian3.clamp(    maxBounds,    defaultMinBounds,    defaultMaxBounds,    this._maxBounds  );  clipMinBounds = Cartesian3.clamp(    clipMinBounds,    defaultMinBounds,    defaultMaxBounds,    scratchClipMinBounds  );  clipMaxBounds = Cartesian3.clamp(    clipMaxBounds,    defaultMinBounds,    defaultMaxBounds,    scratchClipMaxBounds  );  const renderMinBounds = Cartesian3.clamp(    minBounds,    clipMinBounds,    clipMaxBounds,    scratchRenderMinBounds  );  const renderMaxBounds = Cartesian3.clamp(    maxBounds,    clipMinBounds,    clipMaxBounds,    scratchRenderMaxBounds  );  const scale = Matrix4.getScale(modelMatrix, scratchScale);  // Box is not visible if:  // - any of the min render bounds exceed the max render bounds  // - two or more of the min bounds equal the max bounds (line / point)  // - any of the min clip bounds exceed the max clip bounds  // - scale is 0 for any component (too annoying to reconstruct rotation matrix)  if (    renderMinBounds.x > renderMaxBounds.x ||    renderMinBounds.y > renderMaxBounds.y ||    renderMinBounds.z > renderMaxBounds.z ||    (renderMinBounds.x === renderMaxBounds.x) +      (renderMinBounds.y === renderMaxBounds.y) +      (renderMinBounds.z === renderMaxBounds.z) >=      2 ||    clipMinBounds.x > clipMaxBounds.x ||    clipMinBounds.y > clipMaxBounds.y ||    clipMinBounds.z > clipMaxBounds.z ||    scale.x === 0.0 ||    scale.y === 0.0 ||    scale.z === 0.0  ) {    return false;  }  this.shapeTransform = Matrix4.clone(modelMatrix, this.shapeTransform);  this.orientedBoundingBox = getBoxChunkObb(    renderMinBounds,    renderMaxBounds,    this.shapeTransform,    this.orientedBoundingBox  );  // All of the box bounds go from -1 to +1, so the model matrix scale can be  // used as the oriented bounding box half axes.  this.boundTransform = Matrix4.fromRotationTranslation(    this.orientedBoundingBox.halfAxes,    this.orientedBoundingBox.center,    this.boundTransform  );  this.boundingSphere = BoundingSphere.fromOrientedBoundingBox(    this.orientedBoundingBox,    this.boundingSphere  );  const { shaderUniforms, shaderDefines } = this;  // To keep things simple, clear the defines every time  for (const key in shaderDefines) {    if (shaderDefines.hasOwnProperty(key)) {      shaderDefines[key] = undefined;    }  }  const hasShapeBounds =    !Cartesian3.equals(minBounds, defaultMinBounds) ||    !Cartesian3.equals(maxBounds, defaultMaxBounds);  // Keep track of how many intersections there are going to be.  let intersectionCount = 0;  shaderDefines["BOX_INTERSECTION_INDEX"] = intersectionCount;  intersectionCount += 1;  shaderUniforms.renderMinBounds = Matrix4.multiplyByPoint(    transformLocalToUv,    renderMinBounds,    shaderUniforms.renderMinBounds  );  shaderUniforms.renderMaxBounds = Matrix4.multiplyByPoint(    transformLocalToUv,    renderMaxBounds,    shaderUniforms.renderMaxBounds  );  if (hasShapeBounds) {    shaderDefines["BOX_HAS_SHAPE_BOUNDS"] = true;    const min = minBounds;    const max = maxBounds;    // Go from UV space to bounded UV space:    // delerp(posUv, minBoundsUv, maxBoundsUv)    // (posUv - minBoundsUv) / (maxBoundsUv - minBoundsUv)    // posUv / (maxBoundsUv - minBoundsUv) - minBoundsUv / (maxBoundsUv - minBoundsUv)    // scale = 1.0 / (maxBoundsUv - minBoundsUv)    // scale = 1.0 / ((maxBounds * 0.5 + 0.5) - (minBounds * 0.5 + 0.5))    // scale = 2.0 / (maxBounds - minBounds)    // offset = -minBoundsUv / ((maxBounds * 0.5 + 0.5) - (minBounds * 0.5 + 0.5))    // offset = -2.0 * (minBounds * 0.5 + 0.5) / (maxBounds - minBounds)    // offset = -scale * (minBounds * 0.5 + 0.5)    shaderUniforms.boxUvToShapeUvScale = Cartesian3.fromElements(      2.0 / (min.x === max.x ? 1.0 : max.x - min.x),      2.0 / (min.y === max.y ? 1.0 : max.y - min.y),      2.0 / (min.z === max.z ? 1.0 : max.z - min.z),      shaderUniforms.boxUvToShapeUvScale    );    shaderUniforms.boxUvToShapeUvTranslate = Cartesian3.fromElements(      -shaderUniforms.boxUvToShapeUvScale.x * (min.x * 0.5 + 0.5),      -shaderUniforms.boxUvToShapeUvScale.y * (min.y * 0.5 + 0.5),      -shaderUniforms.boxUvToShapeUvScale.z * (min.z * 0.5 + 0.5),      shaderUniforms.boxUvToShapeUvTranslate    );  }  this.shaderMaximumIntersectionsLength = intersectionCount;  return true;};const scratchTileMinBounds = new Cartesian3();const scratchTileMaxBounds = new Cartesian3();/** * Computes an oriented bounding box for a specified tile. * The update function must be called before calling this function. * * @param {number} tileLevel The tile's level. * @param {number} tileX The tile's x coordinate. * @param {number} tileY The tile's y coordinate. * @param {number} tileZ The tile's z coordinate. * @param {OrientedBoundingBox} result The oriented bounding box that will be set to enclose the specified tile * @returns {OrientedBoundingBox} The oriented bounding box. */VoxelBoxShape.prototype.computeOrientedBoundingBoxForTile = function (  tileLevel,  tileX,  tileY,  tileZ,  result) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.number("tileLevel", tileLevel);  Check.typeOf.number("tileX", tileX);  Check.typeOf.number("tileY", tileY);  Check.typeOf.number("tileZ", tileZ);  Check.typeOf.object("result", result);  //>>includeEnd('debug');  const minBounds = this._minBounds;  const maxBounds = this._maxBounds;  const sizeAtLevel = 1.0 / Math.pow(2, tileLevel);  const tileMinBounds = Cartesian3.fromElements(    CesiumMath.lerp(minBounds.x, maxBounds.x, sizeAtLevel * tileX),    CesiumMath.lerp(minBounds.y, maxBounds.y, sizeAtLevel * tileY),    CesiumMath.lerp(minBounds.z, maxBounds.z, sizeAtLevel * tileZ),    scratchTileMinBounds  );  const tileMaxBounds = Cartesian3.fromElements(    CesiumMath.lerp(minBounds.x, maxBounds.x, sizeAtLevel * (tileX + 1)),    CesiumMath.lerp(minBounds.y, maxBounds.y, sizeAtLevel * (tileY + 1)),    CesiumMath.lerp(minBounds.z, maxBounds.z, sizeAtLevel * (tileZ + 1)),    scratchTileMaxBounds  );  return getBoxChunkObb(    tileMinBounds,    tileMaxBounds,    this.shapeTransform,    result  );};/** * Computes an approximate step size for raymarching the root tile of a voxel grid. * The update function must be called before calling this function. * * @param {Cartesian3} dimensions The voxel grid dimensions for a tile. * @returns {number} The step size. */VoxelBoxShape.prototype.computeApproximateStepSize = function (dimensions) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("dimensions", dimensions);  //>>includeEnd('debug');  return 1.0 / Cartesian3.maximumComponent(dimensions);};/** * Defines the minimum bounds of the shape. Corresponds to minimum X, Y, Z. * * @type {Cartesian3} * @constant * @readonly */VoxelBoxShape.DefaultMinBounds = Object.freeze(  new Cartesian3(-1.0, -1.0, -1.0));/** * Defines the maximum bounds of the shape. Corresponds to maximum X, Y, Z. * * @type {Cartesian3} * @constant * @readonly */VoxelBoxShape.DefaultMaxBounds = Object.freeze(  new Cartesian3(+1.0, +1.0, +1.0));/** * Computes an {@link OrientedBoundingBox} for a subregion of the shape. * * @function * * @param {Cartesian3} minimumBounds The minimum bounds, in the local coordinates of the shape. * @param {Cartesian3} maximumBounds The maximum bounds, in the local coordinates of the shape. * @param {Matrix4} matrix The matrix to transform the points. * @param {OrientedBoundingBox} result The object onto which to store the result. * @returns {OrientedBoundingBox} The oriented bounding box that contains this subregion. * * @private */function getBoxChunkObb(minimumBounds, maximumBounds, matrix, result) {  const defaultMinBounds = VoxelBoxShape.DefaultMinBounds;  const defaultMaxBounds = VoxelBoxShape.DefaultMaxBounds;  const isDefaultBounds =    Cartesian3.equals(minimumBounds, defaultMinBounds) &&    Cartesian3.equals(maximumBounds, defaultMaxBounds);  if (isDefaultBounds) {    result.center = Matrix4.getTranslation(matrix, result.center);    result.halfAxes = Matrix4.getMatrix3(matrix, result.halfAxes);  } else {    let scale = Matrix4.getScale(matrix, scratchScale);    const localCenter = Cartesian3.midpoint(      minimumBounds,      maximumBounds,      scratchCenter    );    result.center = Matrix4.multiplyByPoint(matrix, localCenter, result.center);    scale = Cartesian3.fromElements(      scale.x * 0.5 * (maximumBounds.x - minimumBounds.x),      scale.y * 0.5 * (maximumBounds.y - minimumBounds.y),      scale.z * 0.5 * (maximumBounds.z - minimumBounds.z),      scratchScale    );    const rotation = Matrix4.getRotation(matrix, scratchRotation);    result.halfAxes = Matrix3.setScale(rotation, scale, result.halfAxes);  }  return result;}export default VoxelBoxShape;
 |