| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- 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 VoxelBoundsCollection from "./VoxelBoundsCollection.js";
- import ClippingPlane from "./ClippingPlane.js";
-
- /**
- * A box {@link VoxelShape}.
- *
- * @alias VoxelBoxShape
- * @constructor
- *
- * @see VoxelShape
- * @see VoxelEllipsoidShape
- * @see VoxelCylinderShape
- * @see VoxelShapeType
- *
- * @private
- */
- function VoxelBoxShape() {
- this._orientedBoundingBox = new OrientedBoundingBox();
- this._boundingSphere = new BoundingSphere();
- this._boundTransform = new Matrix4();
- this._shapeTransform = new Matrix4();
-
- /**
- * The minimum bounds of the shape.
- * @type {Cartesian3}
- * @private
- */
- this._minBounds = VoxelBoxShape.DefaultMinBounds.clone();
-
- /**
- * The maximum bounds of the shape.
- * @type {Cartesian3}
- * @private
- */
- this._maxBounds = VoxelBoxShape.DefaultMaxBounds.clone();
-
- /**
- * The minimum render bounds of the shape.
- * @type {Cartesian3}
- * @private
- */
- this._renderMinBounds = VoxelBoxShape.DefaultMinBounds.clone();
-
- /**
- * The maximum render bounds of the shape.
- * @type {Cartesian3}
- * @private
- */
- this._renderMaxBounds = VoxelBoxShape.DefaultMaxBounds.clone();
-
- const { DefaultMinBounds, DefaultMaxBounds } = VoxelBoxShape;
- const boundPlanes = [
- new ClippingPlane(
- Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()),
- DefaultMinBounds.x,
- ),
- new ClippingPlane(
- Cartesian3.negate(Cartesian3.UNIT_Y, new Cartesian3()),
- DefaultMinBounds.y,
- ),
- new ClippingPlane(
- Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()),
- DefaultMinBounds.z,
- ),
- new ClippingPlane(Cartesian3.UNIT_X, -DefaultMaxBounds.x),
- new ClippingPlane(Cartesian3.UNIT_Y, -DefaultMaxBounds.y),
- new ClippingPlane(Cartesian3.UNIT_Z, -DefaultMaxBounds.z),
- ];
-
- this._renderBoundPlanes = new VoxelBoundsCollection({ planes: boundPlanes });
-
- /**
- * UV space transformation translation (JS-only, not a shader uniform)
- * @type {Cartesian3}
- * @private
- */
- this._localToShapeUvTranslate = new Cartesian3();
-
- this._shaderUniforms = {
- boxEcToXyz: new Matrix3(),
- boxLocalToShapeUvScale: new Cartesian3(),
- };
-
- this._shaderDefines = {
- BOX_INTERSECTION_INDEX: undefined,
- };
-
- this._shaderMaximumIntersectionsLength = 0; // not known until update
- }
-
- Object.defineProperties(VoxelBoxShape.prototype, {
- /**
- * An oriented bounding box containing the bounded shape.
- *
- * @memberof VoxelBoxShape.prototype
- * @type {OrientedBoundingBox}
- * @readonly
- * @private
- */
- orientedBoundingBox: {
- get: function () {
- return this._orientedBoundingBox;
- },
- },
-
- /**
- * A collection of planes used for the render bounds
- * @memberof VoxelBoxShape.prototype
- * @type {VoxelBoundsCollection}
- * @readonly
- * @private
- */
- renderBoundPlanes: {
- get: function () {
- return this._renderBoundPlanes;
- },
- },
-
- /**
- * A bounding sphere containing the bounded shape.
- *
- * @memberof VoxelBoxShape.prototype
- * @type {BoundingSphere}
- * @readonly
- * @private
- */
- boundingSphere: {
- get: function () {
- return this._boundingSphere;
- },
- },
-
- /**
- * A transformation matrix containing the bounded shape.
- *
- * @memberof VoxelBoxShape.prototype
- * @type {Matrix4}
- * @readonly
- * @private
- */
- boundTransform: {
- get: function () {
- return this._boundTransform;
- },
- },
-
- /**
- * A transformation matrix containing the shape, ignoring the bounds.
- *
- * @memberof VoxelBoxShape.prototype
- * @type {Matrix4}
- * @readonly
- * @private
- */
- shapeTransform: {
- get: function () {
- return this._shapeTransform;
- },
- },
-
- /**
- * @memberof VoxelBoxShape.prototype
- * @type {Object<string, any>}
- * @readonly
- * @private
- */
- shaderUniforms: {
- get: function () {
- return this._shaderUniforms;
- },
- },
-
- /**
- * @memberof VoxelBoxShape.prototype
- * @type {Object<string, any>}
- * @readonly
- * @private
- */
- shaderDefines: {
- get: function () {
- return this._shaderDefines;
- },
- },
-
- /**
- * The maximum number of intersections against the shape for any ray direction.
- * @memberof VoxelBoxShape.prototype
- * @type {number}
- * @readonly
- * @private
- */
- shaderMaximumIntersectionsLength: {
- get: function () {
- return this._shaderMaximumIntersectionsLength;
- },
- },
- });
-
- const scratchCenter = new Cartesian3();
- const scratchScale = new Cartesian3();
- const scratchRotation = new Matrix3();
- const scratchClipMinBounds = new Cartesian3();
- const scratchClipMaxBounds = new Cartesian3();
-
- /**
- * Update the shape's state.
- * @private
- * @param {Matrix4} modelMatrix The model matrix.
- * @param {Cartesian3} minBounds The minimum bounds.
- * @param {Cartesian3} maxBounds The maximum bounds.
- * @param {Cartesian3} [clipMinBounds] The minimum clip bounds.
- * @param {Cartesian3} [clipMaxBounds] The maximum clip bounds.
- * @returns {boolean} Whether the shape is visible.
- */
- VoxelBoxShape.prototype.update = function (
- modelMatrix,
- minBounds,
- maxBounds,
- clipMinBounds,
- clipMaxBounds,
- ) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("modelMatrix", modelMatrix);
- Check.typeOf.object("minBounds", minBounds);
- Check.typeOf.object("maxBounds", maxBounds);
- //>>includeEnd('debug');
-
- clipMinBounds = clipMinBounds ?? minBounds.clone(scratchClipMinBounds);
- clipMaxBounds = clipMaxBounds ?? maxBounds.clone(scratchClipMaxBounds);
-
- minBounds = Cartesian3.clone(minBounds, this._minBounds);
- maxBounds = Cartesian3.clone(maxBounds, this._maxBounds);
-
- const renderMinBounds = Cartesian3.clamp(
- minBounds,
- clipMinBounds,
- clipMaxBounds,
- this._renderMinBounds,
- );
- const renderMaxBounds = Cartesian3.clamp(
- maxBounds,
- clipMinBounds,
- clipMaxBounds,
- this._renderMaxBounds,
- );
-
- // 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)
- // - scale is 0 for any component (too annoying to reconstruct rotation matrix)
- const scale = Matrix4.getScale(modelMatrix, scratchScale);
- 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 ||
- scale.x === 0.0 ||
- scale.y === 0.0 ||
- scale.z === 0.0
- ) {
- return false;
- }
-
- // Update the render bounds planes
- const renderBoundPlanes = this._renderBoundPlanes;
- renderBoundPlanes.get(0).distance = renderMinBounds.x;
- renderBoundPlanes.get(1).distance = renderMinBounds.y;
- renderBoundPlanes.get(2).distance = renderMinBounds.z;
- renderBoundPlanes.get(3).distance = -renderMaxBounds.x;
- renderBoundPlanes.get(4).distance = -renderMaxBounds.y;
- renderBoundPlanes.get(5).distance = -renderMaxBounds.z;
-
- 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 = this._shaderUniforms;
- const shaderDefines = this._shaderDefines;
-
- // To keep things simple, clear the defines every time
- for (const key in shaderDefines) {
- if (shaderDefines.hasOwnProperty(key)) {
- shaderDefines[key] = undefined;
- }
- }
-
- // Keep track of how many intersections there are going to be.
- let intersectionCount = 0;
-
- shaderDefines["BOX_INTERSECTION_INDEX"] = intersectionCount;
- intersectionCount += 1;
-
- // Compute scale and translation to transform from UV space to bounded UV space
- const min = minBounds;
- const max = maxBounds;
- const boxLocalToShapeUvScale = Cartesian3.fromElements(
- boundScale(min.x, max.x),
- boundScale(min.y, max.y),
- boundScale(min.z, max.z),
- shaderUniforms.boxLocalToShapeUvScale,
- );
- this._localToShapeUvTranslate = Cartesian3.negate(
- Cartesian3.multiplyComponents(
- boxLocalToShapeUvScale,
- min,
- this._localToShapeUvTranslate,
- ),
- this._localToShapeUvTranslate,
- );
-
- this._shaderMaximumIntersectionsLength = intersectionCount;
-
- return true;
- };
-
- function boundScale(minBound, maxBound) {
- return CesiumMath.equalsEpsilon(minBound, maxBound, CesiumMath.EPSILON7)
- ? 1.0
- : 1.0 / (maxBound - minBound);
- }
-
- const scratchTransformPositionWorldToLocal = new Matrix4();
- /**
- * Update any view-dependent transforms.
- * @private
- * @param {FrameState} frameState The frame state.
- */
- VoxelBoxShape.prototype.updateViewTransforms = function (frameState) {
- const shaderUniforms = this._shaderUniforms;
- const transformPositionWorldToLocal = Matrix4.inverse(
- this._shapeTransform,
- scratchTransformPositionWorldToLocal,
- );
- const transformDirectionWorldToLocal = Matrix4.getMatrix3(
- transformPositionWorldToLocal,
- shaderUniforms.boxEcToXyz,
- );
- const rotateViewToWorld = frameState.context.uniformState.inverseViewRotation;
- Matrix3.multiply(
- transformDirectionWorldToLocal,
- rotateViewToWorld,
- shaderUniforms.boxEcToXyz,
- );
- };
-
- /**
- * Convert a local coordinate to the shape's UV space.
- * @private
- * @param {Cartesian3} positionLocal The local coordinate to convert.
- * @param {Cartesian3} result The Cartesian3 to store the result in.
- * @returns {Cartesian3} The converted UV coordinate.
- */
- VoxelBoxShape.prototype.convertLocalToShapeUvSpace = function (
- positionLocal,
- result,
- ) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("positionLocal", positionLocal);
- Check.typeOf.object("result", result);
- //>>includeEnd('debug');
-
- const { boxLocalToShapeUvScale } = this._shaderUniforms;
- const boxLocalToShapeUvTranslate = this._localToShapeUvTranslate;
-
- return Cartesian3.add(
- Cartesian3.multiplyComponents(
- positionLocal,
- boxLocalToShapeUvScale,
- result,
- ),
- boxLocalToShapeUvTranslate,
- result,
- );
- };
-
- const scratchTileMinBounds = new Cartesian3();
- const scratchTileMaxBounds = new Cartesian3();
-
- /**
- * Computes an oriented bounding box for a specified tile.
- * @private
- * @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,
- );
- };
-
- const sampleSizeScratch = new Cartesian3();
-
- /**
- * Computes an oriented bounding box for a specified sample within a specified tile.
- * @private
- * @param {SpatialNode} spatialNode The spatial node containing the sample
- * @param {Cartesian3} tileDimensions The size of the tile in number of samples, before padding
- * @param {Cartesian3} tileUv The sample coordinate within the tile
- * @param {OrientedBoundingBox} result The oriented bounding box that will be set to enclose the specified sample
- * @returns {OrientedBoundingBox} The oriented bounding box.
- */
- VoxelBoxShape.prototype.computeOrientedBoundingBoxForSample = function (
- spatialNode,
- tileDimensions,
- tileUv,
- result,
- ) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("spatialNode", spatialNode);
- Check.typeOf.object("tileDimensions", tileDimensions);
- Check.typeOf.object("tileUv", tileUv);
- Check.typeOf.object("result", result);
- //>>includeEnd('debug');
-
- const tileSizeAtLevel = 1.0 / Math.pow(2, spatialNode.level);
- const sampleSize = Cartesian3.divideComponents(
- Cartesian3.ONE,
- tileDimensions,
- sampleSizeScratch,
- );
- const sampleSizeAtLevel = Cartesian3.multiplyByScalar(
- sampleSize,
- tileSizeAtLevel,
- sampleSizeScratch,
- );
-
- const minLerp = Cartesian3.multiplyByScalar(
- Cartesian3.fromElements(
- spatialNode.x + tileUv.x,
- spatialNode.y + tileUv.y,
- spatialNode.z + tileUv.z,
- scratchTileMinBounds,
- ),
- tileSizeAtLevel,
- scratchTileMinBounds,
- );
- const maxLerp = Cartesian3.add(
- minLerp,
- sampleSizeAtLevel,
- scratchTileMaxBounds,
- );
-
- const minBounds = this._minBounds;
- const maxBounds = this._maxBounds;
- const sampleMinBounds = Cartesian3.fromElements(
- CesiumMath.lerp(minBounds.x, maxBounds.x, minLerp.x),
- CesiumMath.lerp(minBounds.y, maxBounds.y, minLerp.y),
- CesiumMath.lerp(minBounds.z, maxBounds.z, minLerp.z),
- scratchTileMinBounds,
- );
- const sampleMaxBounds = Cartesian3.fromElements(
- CesiumMath.lerp(minBounds.x, maxBounds.x, maxLerp.x),
- CesiumMath.lerp(minBounds.y, maxBounds.y, maxLerp.y),
- CesiumMath.lerp(minBounds.z, maxBounds.z, maxLerp.z),
- scratchTileMaxBounds,
- );
-
- return getBoxChunkObb(
- sampleMinBounds,
- sampleMaxBounds,
- this._shapeTransform,
- result,
- );
- };
-
- /**
- * Defines the minimum bounds of the shape. Corresponds to minimum X, Y, Z.
- * @private
- * @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.
- * @private
- * @type {Cartesian3}
- * @constant
- * @readonly
- */
- VoxelBoxShape.DefaultMaxBounds = Object.freeze(
- new Cartesian3(+1.0, +1.0, +1.0),
- );
-
- const scratchBoxScale = new Cartesian3();
- /**
- * 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, scratchBoxScale);
- 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),
- scratchBoxScale,
- );
- const rotation = Matrix4.getRotation(matrix, scratchRotation);
- result.halfAxes = Matrix3.setScale(rotation, scale, result.halfAxes);
- }
-
- return result;
- }
-
- export default VoxelBoxShape;
|