UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

892 lines (815 loc) 29.2 kB
import BoundingSphere from "../Core/BoundingSphere.js"; import Cartesian2 from "../Core/Cartesian2.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Check from "../Core/Check.js"; import Ellipsoid from "../Core/Ellipsoid.js"; import CesiumMath from "../Core/Math.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import OrientedBoundingBox from "../Core/OrientedBoundingBox.js"; import Rectangle from "../Core/Rectangle.js"; /** * An ellipsoid {@link VoxelShape}. * * @alias VoxelEllipsoidShape * @constructor * * @see VoxelShape * @see VoxelBoxShape * @see VoxelCylinderShape * @see VoxelShapeType * * @private */ function VoxelEllipsoidShape() { /** * An oriented bounding box containing the bounded shape. * The update function must be called before accessing this value. * @type {OrientedBoundingBox} * @readonly * @private */ this.orientedBoundingBox = new OrientedBoundingBox(); /** * A bounding sphere containing the bounded shape. * The update function must be called before accessing this value. * @type {BoundingSphere} * @readonly * @private */ this.boundingSphere = new BoundingSphere(); /** * A transformation matrix containing the bounded shape. * The update function must be called before accessing this value. * @type {Matrix4} * @readonly * @private */ 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 * @private */ this.shapeTransform = new Matrix4(); /** * @type {Rectangle} * @private */ this._rectangle = new Rectangle(); /** * @type {number} * @private */ this._minimumHeight = VoxelEllipsoidShape.DefaultMinBounds.z; /** * @type {number} * @private */ this._maximumHeight = VoxelEllipsoidShape.DefaultMaxBounds.z; /** * @type {Ellipsoid} * @private */ this._ellipsoid = new Ellipsoid(); /** * @type {Cartesian3} * @private */ this._translation = new Cartesian3(); /** * @type {Matrix3} * @private */ this._rotation = new Matrix3(); /** * @type {Object<string, any>} * @readonly * @private */ this.shaderUniforms = { ellipsoidRadiiUv: new Cartesian3(), eccentricitySquared: 0.0, evoluteScale: new Cartesian2(), ellipsoidInverseRadiiSquaredUv: new Cartesian3(), ellipsoidRenderLongitudeMinMax: new Cartesian2(), ellipsoidShapeUvLongitudeMinMaxMid: new Cartesian3(), ellipsoidUvToShapeUvLongitude: new Cartesian2(), ellipsoidUvToShapeUvLatitude: new Cartesian2(), ellipsoidRenderLatitudeSinMinMax: new Cartesian2(), ellipsoidInverseHeightDifferenceUv: 0.0, clipMinMaxHeight: new Cartesian2(), }; /** * @type {Object<string, any>} * @readonly * @private */ this.shaderDefines = { ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_UNDER_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_OVER_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY: undefined, ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE: undefined, ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF: undefined, ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE: undefined, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE: undefined, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX: undefined, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN: undefined, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX: undefined, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN: undefined, }; /** * The maximum number of intersections against the shape for any ray direction. * @type {number} * @readonly * @private */ this.shaderMaximumIntersectionsLength = 0; // not known until update } const scratchActualMinBounds = new Cartesian3(); const scratchShapeMinBounds = new Cartesian3(); const scratchShapeMaxBounds = new Cartesian3(); const scratchClipMinBounds = new Cartesian3(); const scratchClipMaxBounds = new Cartesian3(); const scratchRenderMinBounds = new Cartesian3(); const scratchRenderMaxBounds = new Cartesian3(); const scratchScale = new Cartesian3(); const scratchRotationScale = new Matrix3(); const scratchShapeOuterExtent = new Cartesian3(); const scratchRenderOuterExtent = new Cartesian3(); const scratchRenderRectangle = new Rectangle(); /** * 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=VoxelEllipsoidShape.DefaultMinBounds] The minimum clip bounds. * @param {Cartesian3} [clipMaxBounds=VoxelEllipsoidShape.DefaultMaxBounds] The maximum clip bounds. * @returns {boolean} Whether the shape is visible. */ VoxelEllipsoidShape.prototype.update = function ( modelMatrix, minBounds, maxBounds, clipMinBounds, clipMaxBounds, ) { const { DefaultMinBounds, DefaultMaxBounds } = VoxelEllipsoidShape; clipMinBounds = clipMinBounds ?? DefaultMinBounds; clipMaxBounds = clipMaxBounds ?? DefaultMaxBounds; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("modelMatrix", modelMatrix); Check.typeOf.object("minBounds", minBounds); Check.typeOf.object("maxBounds", maxBounds); //>>includeEnd('debug'); const epsilonZeroScale = CesiumMath.EPSILON10; const epsilonLongitudeDiscontinuity = CesiumMath.EPSILON3; // 0.001 radians = 0.05729578 degrees const epsilonLongitude = CesiumMath.EPSILON10; const epsilonLatitude = CesiumMath.EPSILON10; const epsilonLatitudeFlat = CesiumMath.EPSILON3; // 0.001 radians = 0.05729578 degrees // Don't let the height go below the center of the ellipsoid. const radii = Matrix4.getScale(modelMatrix, scratchScale); const actualMinBounds = Cartesian3.clone( DefaultMinBounds, scratchActualMinBounds, ); actualMinBounds.z = -Cartesian3.minimumComponent(radii); const shapeMinBounds = Cartesian3.clamp( minBounds, actualMinBounds, DefaultMaxBounds, scratchShapeMinBounds, ); const shapeMaxBounds = Cartesian3.clamp( maxBounds, actualMinBounds, DefaultMaxBounds, scratchShapeMaxBounds, ); const clampedClipMinBounds = Cartesian3.clamp( clipMinBounds, actualMinBounds, DefaultMaxBounds, scratchClipMinBounds, ); const clampedClipMaxBounds = Cartesian3.clamp( clipMaxBounds, actualMinBounds, DefaultMaxBounds, scratchClipMaxBounds, ); const renderMinBounds = Cartesian3.maximumByComponent( shapeMinBounds, clampedClipMinBounds, scratchRenderMinBounds, ); const renderMaxBounds = Cartesian3.minimumByComponent( shapeMaxBounds, clampedClipMaxBounds, scratchRenderMaxBounds, ); // Compute the farthest a point can be from the center of the ellipsoid. const shapeOuterExtent = Cartesian3.add( radii, Cartesian3.fromElements( shapeMaxBounds.z, shapeMaxBounds.z, shapeMaxBounds.z, scratchShapeOuterExtent, ), scratchShapeOuterExtent, ); const shapeMaxExtent = Cartesian3.maximumComponent(shapeOuterExtent); const renderOuterExtent = Cartesian3.add( radii, Cartesian3.fromElements( renderMaxBounds.z, renderMaxBounds.z, renderMaxBounds.z, scratchRenderOuterExtent, ), scratchRenderOuterExtent, ); // Exit early if the shape is not visible. // Note that minLongitude may be greater than maxLongitude when crossing the 180th meridian. if ( renderMinBounds.y > renderMaxBounds.y || renderMinBounds.y === DefaultMaxBounds.y || renderMaxBounds.y === DefaultMinBounds.y || renderMinBounds.z > renderMaxBounds.z || CesiumMath.equalsEpsilon( renderOuterExtent, Cartesian3.ZERO, undefined, epsilonZeroScale, ) ) { return false; } this._rectangle = Rectangle.fromRadians( shapeMinBounds.x, shapeMinBounds.y, shapeMaxBounds.x, shapeMaxBounds.y, ); this._translation = Matrix4.getTranslation(modelMatrix, this._translation); this._rotation = Matrix4.getRotation(modelMatrix, this._rotation); this._ellipsoid = Ellipsoid.fromCartesian3(radii, this._ellipsoid); this._minimumHeight = shapeMinBounds.z; this._maximumHeight = shapeMaxBounds.z; const renderRectangle = Rectangle.fromRadians( renderMinBounds.x, renderMinBounds.y, renderMaxBounds.x, renderMaxBounds.y, scratchRenderRectangle, ); this.orientedBoundingBox = getEllipsoidChunkObb( renderRectangle, renderMinBounds.z, renderMaxBounds.z, this._ellipsoid, this._translation, this._rotation, this.orientedBoundingBox, ); this.shapeTransform = Matrix4.fromRotationTranslation( Matrix3.setScale(this._rotation, shapeOuterExtent, scratchRotationScale), this._translation, this.shapeTransform, ); this.boundTransform = Matrix4.fromRotationTranslation( this.orientedBoundingBox.halfAxes, this.orientedBoundingBox.center, this.boundTransform, ); this.boundingSphere = BoundingSphere.fromOrientedBoundingBox( this.orientedBoundingBox, this.boundingSphere, ); // Longitude const defaultLongitudeRange = DefaultMaxBounds.x - DefaultMinBounds.x; const defaultLongitudeRangeHalf = 0.5 * defaultLongitudeRange; const renderIsLongitudeReversed = renderMaxBounds.x < renderMinBounds.x; const renderLongitudeRange = renderMaxBounds.x - renderMinBounds.x + renderIsLongitudeReversed * defaultLongitudeRange; const renderIsLongitudeRangeZero = renderLongitudeRange <= epsilonLongitude; const renderIsLongitudeRangeUnderHalf = renderLongitudeRange >= defaultLongitudeRangeHalf - epsilonLongitude && renderLongitudeRange < defaultLongitudeRange - epsilonLongitude; const renderIsLongitudeRangeOverHalf = renderLongitudeRange > epsilonLongitude && renderLongitudeRange < defaultLongitudeRangeHalf - epsilonLongitude; const renderHasLongitude = renderIsLongitudeRangeZero || renderIsLongitudeRangeUnderHalf || renderIsLongitudeRangeOverHalf; const shapeIsLongitudeReversed = shapeMaxBounds.x < shapeMinBounds.x; const shapeLongitudeRange = shapeMaxBounds.x - shapeMinBounds.x + shapeIsLongitudeReversed * defaultLongitudeRange; const shapeIsLongitudeRangeUnderHalf = shapeLongitudeRange > defaultLongitudeRangeHalf + epsilonLongitude && shapeLongitudeRange < defaultLongitudeRange - epsilonLongitude; const shapeIsLongitudeRangeHalf = shapeLongitudeRange >= defaultLongitudeRangeHalf - epsilonLongitude && shapeLongitudeRange <= defaultLongitudeRangeHalf + epsilonLongitude; const shapeIsLongitudeRangeOverHalf = shapeLongitudeRange < defaultLongitudeRangeHalf - epsilonLongitude; const shapeHasLongitude = shapeIsLongitudeRangeUnderHalf || shapeIsLongitudeRangeHalf || shapeIsLongitudeRangeOverHalf; // Latitude const renderIsLatitudeMaxUnderHalf = renderMaxBounds.y < -epsilonLatitudeFlat; const renderIsLatitudeMaxHalf = renderMaxBounds.y >= -epsilonLatitudeFlat && renderMaxBounds.y <= +epsilonLatitudeFlat; const renderIsLatitudeMaxOverHalf = renderMaxBounds.y > +epsilonLatitudeFlat && renderMaxBounds.y < DefaultMaxBounds.y - epsilonLatitude; const renderHasLatitudeMax = renderIsLatitudeMaxUnderHalf || renderIsLatitudeMaxHalf || renderIsLatitudeMaxOverHalf; const renderIsLatitudeMinUnderHalf = renderMinBounds.y > DefaultMinBounds.y + epsilonLatitude && renderMinBounds.y < -epsilonLatitudeFlat; const renderIsLatitudeMinHalf = renderMinBounds.y >= -epsilonLatitudeFlat && renderMinBounds.y <= +epsilonLatitudeFlat; const renderIsLatitudeMinOverHalf = renderMinBounds.y > +epsilonLatitudeFlat; const renderHasLatitudeMin = renderIsLatitudeMinUnderHalf || renderIsLatitudeMinHalf || renderIsLatitudeMinOverHalf; const renderHasLatitude = renderHasLatitudeMax || renderHasLatitudeMin; const shapeLatitudeRange = shapeMaxBounds.y - shapeMinBounds.y; const shapeIsLatitudeMaxUnderHalf = shapeMaxBounds.y < -epsilonLatitudeFlat; const shapeIsLatitudeMaxHalf = shapeMaxBounds.y >= -epsilonLatitudeFlat && shapeMaxBounds.y <= +epsilonLatitudeFlat; const shapeIsLatitudeMaxOverHalf = shapeMaxBounds.y > +epsilonLatitudeFlat && shapeMaxBounds.y < DefaultMaxBounds.y - epsilonLatitude; const shapeHasLatitudeMax = shapeIsLatitudeMaxUnderHalf || shapeIsLatitudeMaxHalf || shapeIsLatitudeMaxOverHalf; const shapeIsLatitudeMinUnderHalf = shapeMinBounds.y > DefaultMinBounds.y + epsilonLatitude && shapeMinBounds.y < -epsilonLatitudeFlat; const shapeIsLatitudeMinHalf = shapeMinBounds.y >= -epsilonLatitudeFlat && shapeMinBounds.y <= +epsilonLatitudeFlat; const shapeIsLatitudeMinOverHalf = shapeMinBounds.y > +epsilonLatitudeFlat; const shapeHasLatitudeMin = shapeIsLatitudeMinUnderHalf || shapeIsLatitudeMinHalf || shapeIsLatitudeMinOverHalf; const shapeHasLatitude = shapeHasLatitudeMax || shapeHasLatitudeMin; 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; } } // The ellipsoid radii scaled to [0,1]. The max ellipsoid radius will be 1.0 and others will be less. shaderUniforms.ellipsoidRadiiUv = Cartesian3.divideByScalar( shapeOuterExtent, shapeMaxExtent, shaderUniforms.ellipsoidRadiiUv, ); const { x: radiiUvX, z: radiiUvZ } = shaderUniforms.ellipsoidRadiiUv; const axisRatio = radiiUvZ / radiiUvX; shaderUniforms.eccentricitySquared = 1.0 - axisRatio * axisRatio; shaderUniforms.evoluteScale = Cartesian2.fromElements( (radiiUvX * radiiUvX - radiiUvZ * radiiUvZ) / radiiUvX, (radiiUvZ * radiiUvZ - radiiUvX * radiiUvX) / radiiUvZ, shaderUniforms.evoluteScale, ); // Used to compute geodetic surface normal. shaderUniforms.ellipsoidInverseRadiiSquaredUv = Cartesian3.divideComponents( Cartesian3.ONE, Cartesian3.multiplyComponents( shaderUniforms.ellipsoidRadiiUv, shaderUniforms.ellipsoidRadiiUv, shaderUniforms.ellipsoidInverseRadiiSquaredUv, ), shaderUniforms.ellipsoidInverseRadiiSquaredUv, ); // Keep track of how many intersections there are going to be. let intersectionCount = 0; // Intersects outer and inner ellipsoid for the max and min height. shaderDefines["ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX"] = intersectionCount; intersectionCount += 1; shaderDefines["ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN"] = intersectionCount; intersectionCount += 1; shaderUniforms.clipMinMaxHeight = Cartesian2.fromElements( (renderMinBounds.z - shapeMaxBounds.z) / shapeMaxExtent, (renderMaxBounds.z - shapeMaxBounds.z) / shapeMaxExtent, shaderUniforms.clipMinMaxHeight, ); // The percent of space that is between the inner and outer ellipsoid. const thickness = (shapeMaxBounds.z - shapeMinBounds.z) / shapeMaxExtent; shaderUniforms.ellipsoidInverseHeightDifferenceUv = 1.0 / thickness; if (shapeMinBounds.z === shapeMaxBounds.z) { shaderUniforms.ellipsoidInverseHeightDifferenceUv = 0.0; } // Intersects a wedge for the min and max longitude. if (renderHasLongitude) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE"] = true; shaderDefines["ELLIPSOID_INTERSECTION_INDEX_LONGITUDE"] = intersectionCount; if (renderIsLongitudeRangeUnderHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_UNDER_HALF"] = true; intersectionCount += 1; } else if (renderIsLongitudeRangeOverHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_OVER_HALF"] = true; intersectionCount += 2; } else if (renderIsLongitudeRangeZero) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO"] = true; intersectionCount += 2; } shaderUniforms.ellipsoidRenderLongitudeMinMax = Cartesian2.fromElements( renderMinBounds.x, renderMaxBounds.x, shaderUniforms.ellipsoidRenderLongitudeMinMax, ); } if (shapeHasLongitude) { shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE"] = true; const shapeIsLongitudeReversed = shapeMaxBounds.x < shapeMinBounds.x; if (shapeIsLongitudeReversed) { shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED"] = true; } // delerp(longitudeUv, minLongitudeUv, maxLongitudeUv) // (longitudeUv - minLongitudeUv) / (maxLongitudeUv - minLongitudeUv) // longitudeUv / (maxLongitudeUv - minLongitudeUv) - minLongitudeUv / (maxLongitudeUv - minLongitudeUv) // scale = 1.0 / (maxLongitudeUv - minLongitudeUv) // scale = 1.0 / (((maxLongitude - pi) / (2.0 * pi)) - ((minLongitude - pi) / (2.0 * pi))) // scale = 2.0 * pi / (maxLongitude - minLongitude) // offset = -minLongitudeUv / (maxLongitudeUv - minLongitudeUv) // offset = -((minLongitude - pi) / (2.0 * pi)) / (((maxLongitude - pi) / (2.0 * pi)) - ((minLongitude - pi) / (2.0 * pi))) // offset = -(minLongitude - pi) / (maxLongitude - minLongitude) if (shapeLongitudeRange <= epsilonLongitude) { shaderUniforms.ellipsoidUvToShapeUvLongitude = Cartesian2.fromElements( 0.0, 1.0, shaderUniforms.ellipsoidUvToShapeUvLongitude, ); } else { const scale = defaultLongitudeRange / shapeLongitudeRange; const offset = -(shapeMinBounds.x - DefaultMinBounds.x) / shapeLongitudeRange; shaderUniforms.ellipsoidUvToShapeUvLongitude = Cartesian2.fromElements( scale, offset, shaderUniforms.ellipsoidUvToShapeUvLongitude, ); } } if (renderHasLongitude) { const renderIsMinLongitudeDiscontinuity = CesiumMath.equalsEpsilon( renderMinBounds.x, DefaultMinBounds.x, undefined, epsilonLongitudeDiscontinuity, ); const renderIsMaxLongitudeDiscontinuity = CesiumMath.equalsEpsilon( renderMaxBounds.x, DefaultMaxBounds.x, undefined, epsilonLongitudeDiscontinuity, ); if (renderIsMinLongitudeDiscontinuity) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY"] = true; } if (renderIsMaxLongitudeDiscontinuity) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY"] = true; } const uvShapeMinLongitude = (shapeMinBounds.x - DefaultMinBounds.x) / defaultLongitudeRange; const uvShapeMaxLongitude = (shapeMaxBounds.x - DefaultMinBounds.x) / defaultLongitudeRange; const uvRenderMaxLongitude = (renderMaxBounds.x - DefaultMinBounds.x) / defaultLongitudeRange; const uvRenderLongitudeRangeZero = 1.0 - renderLongitudeRange / defaultLongitudeRange; const uvRenderLongitudeRangeZeroMid = (uvRenderMaxLongitude + 0.5 * uvRenderLongitudeRangeZero) % 1.0; shaderUniforms.ellipsoidShapeUvLongitudeMinMaxMid = Cartesian3.fromElements( uvShapeMinLongitude, uvShapeMaxLongitude, uvRenderLongitudeRangeZeroMid, shaderUniforms.ellipsoidShapeUvLongitudeMinMaxMid, ); } if (renderHasLatitude) { // Intersects a cone for min latitude if (renderHasLatitudeMin) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN"] = true; shaderDefines["ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN"] = intersectionCount; if (renderIsLatitudeMinUnderHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF"] = true; intersectionCount += 1; } else if (renderIsLatitudeMinHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF"] = true; intersectionCount += 1; } else if (renderIsLatitudeMinOverHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF"] = true; intersectionCount += 2; } } // Intersects a cone for max latitude if (renderHasLatitudeMax) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX"] = true; shaderDefines["ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX"] = intersectionCount; if (renderIsLatitudeMaxUnderHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF"] = true; intersectionCount += 2; } else if (renderIsLatitudeMaxHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF"] = true; intersectionCount += 1; } else if (renderIsLatitudeMaxOverHalf) { shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF"] = true; intersectionCount += 1; } } shaderUniforms.ellipsoidRenderLatitudeSinMinMax = Cartesian2.fromElements( Math.sin(renderMinBounds.y), Math.sin(renderMaxBounds.y), shaderUniforms.ellipsoidRenderLatitudeSinMinMax, ); } if (shapeHasLatitude) { shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE"] = true; // delerp(latitudeUv, minLatitudeUv, maxLatitudeUv) // (latitudeUv - minLatitudeUv) / (maxLatitudeUv - minLatitudeUv) // latitudeUv / (maxLatitudeUv - minLatitudeUv) - minLatitudeUv / (maxLatitudeUv - minLatitudeUv) // scale = 1.0 / (maxLatitudeUv - minLatitudeUv) // scale = 1.0 / (((maxLatitude - pi) / (2.0 * pi)) - ((minLatitude - pi) / (2.0 * pi))) // scale = 2.0 * pi / (maxLatitude - minLatitude) // offset = -minLatitudeUv / (maxLatitudeUv - minLatitudeUv) // offset = -((minLatitude - -pi) / (2.0 * pi)) / (((maxLatitude - pi) / (2.0 * pi)) - ((minLatitude - pi) / (2.0 * pi))) // offset = -(minLatitude - -pi) / (maxLatitude - minLatitude) // offset = (-pi - minLatitude) / (maxLatitude - minLatitude) if (shapeLatitudeRange < epsilonLatitude) { shaderUniforms.ellipsoidUvToShapeUvLatitude = Cartesian2.fromElements( 0.0, 1.0, shaderUniforms.ellipsoidUvToShapeUvLatitude, ); } else { const defaultLatitudeRange = DefaultMaxBounds.y - DefaultMinBounds.y; const scale = defaultLatitudeRange / shapeLatitudeRange; const offset = (DefaultMinBounds.y - shapeMinBounds.y) / shapeLatitudeRange; shaderUniforms.ellipsoidUvToShapeUvLatitude = Cartesian2.fromElements( scale, offset, shaderUniforms.ellipsoidUvToShapeUvLatitude, ); } } this.shaderMaximumIntersectionsLength = intersectionCount; return true; }; const scratchRectangle = new Rectangle(); /** * Computes an oriented bounding box for a specified tile. * The update function must be called before calling this function. * @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. */ VoxelEllipsoidShape.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 sizeAtLevel = 1.0 / Math.pow(2.0, tileLevel); const minLongitudeLerp = tileX * sizeAtLevel; const maxLongitudeLerp = (tileX + 1) * sizeAtLevel; const minLatitudeLerp = tileY * sizeAtLevel; const maxLatitudeLerp = (tileY + 1) * sizeAtLevel; const minHeightLerp = tileZ * sizeAtLevel; const maxHeightLerp = (tileZ + 1) * sizeAtLevel; const rectangle = Rectangle.subsection( this._rectangle, minLongitudeLerp, minLatitudeLerp, maxLongitudeLerp, maxLatitudeLerp, scratchRectangle, ); const minHeight = CesiumMath.lerp( this._minimumHeight, this._maximumHeight, minHeightLerp, ); const maxHeight = CesiumMath.lerp( this._minimumHeight, this._maximumHeight, maxHeightLerp, ); return getEllipsoidChunkObb( rectangle, minHeight, maxHeight, this._ellipsoid, this._translation, this._rotation, result, ); }; const sampleSizeScratch = new Cartesian3(); const scratchTileMinBounds = new Cartesian3(); const scratchTileMaxBounds = new Cartesian3(); /** * Computes an oriented bounding box for a specified sample within a specified tile. * The update function must be called before calling this function. * @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. */ VoxelEllipsoidShape.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.0, 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 rectangle = Rectangle.subsection( this._rectangle, minLerp.x, minLerp.y, maxLerp.x, maxLerp.y, scratchRectangle, ); const minHeight = CesiumMath.lerp( this._minimumHeight, this._maximumHeight, minLerp.z, ); const maxHeight = CesiumMath.lerp( this._minimumHeight, this._maximumHeight, maxLerp.z, ); return getEllipsoidChunkObb( rectangle, minHeight, maxHeight, this._ellipsoid, this._translation, this._rotation, result, ); }; /** * Computes an {@link OrientedBoundingBox} for a subregion of the shape. * * @function * * @param {Rectangle} rectangle The rectangle. * @param {number} minHeight The minimumZ. * @param {number} maxHeight The maximumZ. * @param {Ellipsoid} ellipsoid The ellipsoid. * @param {Cartesian3} translation The translation applied to the shape * @param {Matrix3} rotation The rotation applied to the shape * @param {OrientedBoundingBox} result The object onto which to store the result. * @returns {OrientedBoundingBox} The oriented bounding box that contains this subregion. * * @private */ function getEllipsoidChunkObb( rectangle, minHeight, maxHeight, ellipsoid, translation, rotation, result, ) { result = OrientedBoundingBox.fromRectangle( rectangle, minHeight, maxHeight, ellipsoid, result, ); result.center = Cartesian3.add(result.center, translation, result.center); result.halfAxes = Matrix3.multiply( result.halfAxes, rotation, result.halfAxes, ); return result; } /** * Defines the minimum bounds of the shape. Corresponds to minimum longitude, latitude, height. * @private * @type {Cartesian3} * @constant * @readonly */ VoxelEllipsoidShape.DefaultMinBounds = Object.freeze( new Cartesian3( -CesiumMath.PI, -CesiumMath.PI_OVER_TWO, -Ellipsoid.WGS84.minimumRadius, ), ); /** * Defines the maximum bounds of the shape. Corresponds to maximum longitude, latitude, height. * @private * @type {Cartesian3} * @constant * @readonly */ VoxelEllipsoidShape.DefaultMaxBounds = Object.freeze( new Cartesian3( CesiumMath.PI, CesiumMath.PI_OVER_TWO, 10.0 * Ellipsoid.WGS84.maximumRadius, ), ); export default VoxelEllipsoidShape;