@loaders.gl/tiles
Version:
Common components for different tiles loaders.
1,419 lines (1,402 loc) • 88.5 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
LOD_METRIC_TYPE: () => LOD_METRIC_TYPE,
TILESET_TYPE: () => TILESET_TYPE,
TILE_CONTENT_STATE: () => TILE_CONTENT_STATE,
TILE_REFINEMENT: () => TILE_REFINEMENT,
TILE_TYPE: () => TILE_TYPE,
Tile3D: () => Tile3D,
Tileset3D: () => Tileset3D,
TilesetCache: () => TilesetCache,
TilesetTraverser: () => TilesetTraverser,
calculateTransformProps: () => calculateTransformProps,
createBoundingVolume: () => createBoundingVolume,
getFrameState: () => getFrameState,
getLodStatus: () => getLodStatus
});
module.exports = __toCommonJS(dist_exports);
// dist/tileset/tileset-3d.js
var import_core10 = require("@math.gl/core");
var import_geospatial6 = require("@math.gl/geospatial");
var import_stats = require("@probe.gl/stats");
var import_loader_utils4 = require("@loaders.gl/loader-utils");
// dist/utils/doubly-linked-list-node.js
var DoublyLinkedListNode = class {
item;
previous;
next;
constructor(item, previous, next) {
this.item = item;
this.previous = previous;
this.next = next;
}
};
// dist/utils/doubly-linked-list.js
var DoublyLinkedList = class {
head = null;
tail = null;
_length = 0;
get length() {
return this._length;
}
/**
* Adds the item to the end of the list
* @param {*} [item]
* @return {DoublyLinkedListNode}
*/
add(item) {
const node = new DoublyLinkedListNode(item, this.tail, null);
if (this.tail) {
this.tail.next = node;
this.tail = node;
} else {
this.head = node;
this.tail = node;
}
++this._length;
return node;
}
/**
* Removes the given node from the list
* @param {DoublyLinkedListNode} node
*/
remove(node) {
if (!node) {
return;
}
if (node.previous && node.next) {
node.previous.next = node.next;
node.next.previous = node.previous;
} else if (node.previous) {
node.previous.next = null;
this.tail = node.previous;
} else if (node.next) {
node.next.previous = null;
this.head = node.next;
} else {
this.head = null;
this.tail = null;
}
node.next = null;
node.previous = null;
--this._length;
}
/**
* Moves nextNode after node
* @param {DoublyLinkedListNode} node
* @param {DoublyLinkedListNode} nextNode
*/
splice(node, nextNode) {
if (node === nextNode) {
return;
}
this.remove(nextNode);
this._insert(node, nextNode);
}
_insert(node, nextNode) {
const oldNodeNext = node.next;
node.next = nextNode;
if (this.tail === node) {
this.tail = nextNode;
} else {
oldNodeNext.previous = nextNode;
}
nextNode.next = oldNodeNext;
nextNode.previous = node;
++this._length;
}
};
// dist/tileset/tileset-cache.js
var TilesetCache = class {
_list;
_sentinel;
_trimTiles;
constructor() {
this._list = new DoublyLinkedList();
this._sentinel = this._list.add("sentinel");
this._trimTiles = false;
}
reset() {
this._list.splice(this._list.tail, this._sentinel);
}
touch(tile) {
const node = tile._cacheNode;
if (node) {
this._list.splice(this._sentinel, node);
}
}
add(tileset, tile, addCallback) {
if (!tile._cacheNode) {
tile._cacheNode = this._list.add(tile);
if (addCallback) {
addCallback(tileset, tile);
}
}
}
unloadTile(tileset, tile, unloadCallback) {
const node = tile._cacheNode;
if (!node) {
return;
}
this._list.remove(node);
tile._cacheNode = null;
if (unloadCallback) {
unloadCallback(tileset, tile);
}
}
unloadTiles(tileset, unloadCallback) {
const trimTiles = this._trimTiles;
this._trimTiles = false;
const list = this._list;
const maximumMemoryUsageInBytes = tileset.maximumMemoryUsage * 1024 * 1024;
const sentinel = this._sentinel;
let node = list.head;
while (node !== sentinel && (tileset.gpuMemoryUsageInBytes > maximumMemoryUsageInBytes || trimTiles)) {
const tile = node.item;
node = node.next;
this.unloadTile(tileset, tile, unloadCallback);
}
}
trim() {
this._trimTiles = true;
}
};
// dist/tileset/helpers/transform-utils.js
var import_geospatial = require("@math.gl/geospatial");
var import_core = require("@math.gl/core");
var import_loader_utils = require("@loaders.gl/loader-utils");
function calculateTransformProps(tileHeader, tile) {
(0, import_loader_utils.assert)(tileHeader);
(0, import_loader_utils.assert)(tile);
const { rtcCenter, gltfUpAxis } = tile;
const { computedTransform, boundingVolume: { center } } = tileHeader;
let modelMatrix = new import_core.Matrix4(computedTransform);
if (rtcCenter) {
modelMatrix.translate(rtcCenter);
}
switch (gltfUpAxis) {
case "Z":
break;
case "Y":
const rotationY = new import_core.Matrix4().rotateX(Math.PI / 2);
modelMatrix = modelMatrix.multiplyRight(rotationY);
break;
case "X":
const rotationX = new import_core.Matrix4().rotateY(-Math.PI / 2);
modelMatrix = modelMatrix.multiplyRight(rotationX);
break;
default:
break;
}
if (tile.isQuantized) {
modelMatrix.translate(tile.quantizedVolumeOffset).scale(tile.quantizedVolumeScale);
}
const cartesianOrigin = new import_core.Vector3(center);
tile.cartesianModelMatrix = modelMatrix;
tile.cartesianOrigin = cartesianOrigin;
const cartographicOrigin = import_geospatial.Ellipsoid.WGS84.cartesianToCartographic(cartesianOrigin, new import_core.Vector3());
const fromFixedFrameMatrix = import_geospatial.Ellipsoid.WGS84.eastNorthUpToFixedFrame(cartesianOrigin);
const toFixedFrameMatrix = fromFixedFrameMatrix.invert();
tile.cartographicModelMatrix = toFixedFrameMatrix.multiplyRight(modelMatrix);
tile.cartographicOrigin = cartographicOrigin;
if (!tile.coordinateSystem) {
tile.modelMatrix = tile.cartographicModelMatrix;
}
}
// dist/tileset/helpers/frame-state.js
var import_core2 = require("@math.gl/core");
var import_culling = require("@math.gl/culling");
var import_geospatial2 = require("@math.gl/geospatial");
var scratchVector = new import_core2.Vector3();
var scratchPosition = new import_core2.Vector3();
var cullingVolume = new import_culling.CullingVolume([
new import_culling.Plane(),
new import_culling.Plane(),
new import_culling.Plane(),
new import_culling.Plane(),
new import_culling.Plane(),
new import_culling.Plane()
]);
function getFrameState(viewport, frameNumber) {
const { cameraDirection, cameraUp, height } = viewport;
const { metersPerUnit } = viewport.distanceScales;
const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
const enuToFixedTransform = import_geospatial2.Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
const cameraPositionCartesian2 = import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, new import_core2.Vector3());
const cameraDirectionCartesian = new import_core2.Vector3(
// @ts-ignore
enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraDirection).scale(metersPerUnit))
).normalize();
const cameraUpCartesian = new import_core2.Vector3(
// @ts-ignore
enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraUp).scale(metersPerUnit))
).normalize();
commonSpacePlanesToWGS84(viewport);
const ViewportClass = viewport.constructor;
const { longitude, latitude, width, bearing, zoom } = viewport;
const topDownViewport = new ViewportClass({
longitude,
latitude,
height,
width,
bearing,
zoom,
pitch: 0
});
return {
camera: {
position: cameraPositionCartesian2,
direction: cameraDirectionCartesian,
up: cameraUpCartesian
},
viewport,
topDownViewport,
height,
cullingVolume,
frameNumber,
// TODO: This can be the same between updates, what number is unique for between updates?
sseDenominator: 1.15
// Assumes fovy = 60 degrees
};
}
function limitSelectedTiles(tiles, frameState, maximumTilesSelected) {
if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
return [tiles, []];
}
const tuples = [];
const { longitude: viewportLongitude, latitude: viewportLatitude } = frameState.viewport;
for (const [index, tile] of tiles.entries()) {
const [longitude, latitude] = tile.header.mbs;
const deltaLon = Math.abs(viewportLongitude - longitude);
const deltaLat = Math.abs(viewportLatitude - latitude);
const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
tuples.push([index, distance]);
}
const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
const selectedTiles = [];
for (let i = 0; i < maximumTilesSelected; i++) {
selectedTiles.push(tiles[tuplesSorted[i][0]]);
}
const unselectedTiles = [];
for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
unselectedTiles.push(tiles[tuplesSorted[i][0]]);
}
return [selectedTiles, unselectedTiles];
}
function commonSpacePlanesToWGS84(viewport) {
const frustumPlanes = viewport.getFrustumPlanes();
const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
let i = 0;
cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
for (const dir in frustumPlanes) {
if (dir === "near") {
continue;
}
const plane = frustumPlanes[dir];
const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
cullingVolume.planes[i++].fromPointNormal(
cartesianPos,
// Want the normal to point into the frustum since that's what culling expects
scratchVector.copy(nearCenterCartesian).subtract(cartesianPos)
);
}
}
function closestPointOnPlane(plane, refPoint, out = new import_core2.Vector3()) {
const distanceToRef = plane.normal.dot(refPoint);
out.copy(plane.normal).scale(plane.distance - distanceToRef).add(refPoint);
return out;
}
function worldToCartesian(viewport, point, out = new import_core2.Vector3()) {
const cartographicPos = viewport.unprojectPosition(point);
return import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
}
// dist/tileset/helpers/zoom.js
var import_core3 = require("@math.gl/core");
var import_culling2 = require("@math.gl/culling");
var import_geospatial3 = require("@math.gl/geospatial");
var WGS84_RADIUS_X = 6378137;
var WGS84_RADIUS_Y = 6378137;
var WGS84_RADIUS_Z = 6356752314245179e-9;
var scratchVector2 = new import_core3.Vector3();
function getZoomFromBoundingVolume(boundingVolume, cartorgraphicCenter) {
if (boundingVolume instanceof import_culling2.OrientedBoundingBox) {
const { halfAxes } = boundingVolume;
const obbSize = getObbSize(halfAxes);
return Math.log2(WGS84_RADIUS_Z / (obbSize + cartorgraphicCenter[2]));
} else if (boundingVolume instanceof import_culling2.BoundingSphere) {
const { radius } = boundingVolume;
return Math.log2(WGS84_RADIUS_Z / (radius + cartorgraphicCenter[2]));
} else if (boundingVolume.width && boundingVolume.height) {
const { width, height } = boundingVolume;
const zoomX = Math.log2(WGS84_RADIUS_X / width);
const zoomY = Math.log2(WGS84_RADIUS_Y / height);
return (zoomX + zoomY) / 2;
}
return 1;
}
function getZoomFromFullExtent(fullExtent, cartorgraphicCenter, cartesianCenter) {
import_geospatial3.Ellipsoid.WGS84.cartographicToCartesian([fullExtent.xmax, fullExtent.ymax, fullExtent.zmax], scratchVector2);
const extentSize = Math.sqrt(Math.pow(scratchVector2[0] - cartesianCenter[0], 2) + Math.pow(scratchVector2[1] - cartesianCenter[1], 2) + Math.pow(scratchVector2[2] - cartesianCenter[2], 2));
return Math.log2(WGS84_RADIUS_Z / (extentSize + cartorgraphicCenter[2]));
}
function getZoomFromExtent(extent, cartorgraphicCenter, cartesianCenter) {
const [xmin, ymin, xmax, ymax] = extent;
return getZoomFromFullExtent({ xmin, xmax, ymin, ymax, zmin: 0, zmax: 0 }, cartorgraphicCenter, cartesianCenter);
}
function getObbSize(halfAxes) {
halfAxes.getColumn(0, scratchVector2);
const axeY = halfAxes.getColumn(1);
const axeZ = halfAxes.getColumn(2);
const farthestVertex = scratchVector2.add(axeY).add(axeZ);
const size = farthestVertex.len();
return size;
}
// dist/tileset/tile-3d.js
var import_core7 = require("@math.gl/core");
var import_culling4 = require("@math.gl/culling");
var import_core8 = require("@loaders.gl/core");
// dist/constants.js
var TILE_CONTENT_STATE = {
UNLOADED: 0,
// Has never been requested
LOADING: 1,
// Is waiting on a pending request
PROCESSING: 2,
// Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
READY: 3,
// Ready to render.
EXPIRED: 4,
// Is expired and will be unloaded once new content is loaded.
FAILED: 5
// Request failed.
};
var TILE_REFINEMENT;
(function(TILE_REFINEMENT2) {
TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
})(TILE_REFINEMENT || (TILE_REFINEMENT = {}));
var TILE_TYPE;
(function(TILE_TYPE2) {
TILE_TYPE2["EMPTY"] = "empty";
TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
TILE_TYPE2["POINTCLOUD"] = "pointcloud";
TILE_TYPE2["MESH"] = "mesh";
})(TILE_TYPE || (TILE_TYPE = {}));
var TILESET_TYPE;
(function(TILESET_TYPE2) {
TILESET_TYPE2["I3S"] = "I3S";
TILESET_TYPE2["TILES3D"] = "TILES3D";
})(TILESET_TYPE || (TILESET_TYPE = {}));
var LOD_METRIC_TYPE;
(function(LOD_METRIC_TYPE2) {
LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
})(LOD_METRIC_TYPE || (LOD_METRIC_TYPE = {}));
var TILE3D_OPTIMIZATION_HINT = {
NOT_COMPUTED: -1,
USE_OPTIMIZATION: 1,
SKIP_OPTIMIZATION: 0
};
// dist/tileset/helpers/bounding-volume.js
var import_core4 = require("@math.gl/core");
var import_culling3 = require("@math.gl/culling");
var import_geospatial4 = require("@math.gl/geospatial");
var import_loader_utils2 = require("@loaders.gl/loader-utils");
function defined(x) {
return x !== void 0 && x !== null;
}
var scratchPoint = new import_core4.Vector3();
var scratchScale = new import_core4.Vector3();
var scratchNorthWest = new import_core4.Vector3();
var scratchSouthEast = new import_core4.Vector3();
var scratchCenter = new import_core4.Vector3();
var scratchXAxis = new import_core4.Vector3();
var scratchYAxis = new import_core4.Vector3();
var scratchZAxis = new import_core4.Vector3();
function createBoundingVolume(boundingVolumeHeader, transform, result) {
(0, import_loader_utils2.assert)(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
if (boundingVolumeHeader.box) {
return createBox(boundingVolumeHeader.box, transform, result);
}
if (boundingVolumeHeader.region) {
return createObbFromRegion(boundingVolumeHeader.region);
}
if (boundingVolumeHeader.sphere) {
return createSphere(boundingVolumeHeader.sphere, transform, result);
}
throw new Error("3D Tile: boundingVolume must contain a sphere, region, or box");
}
function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
if (boundingVolumeHeader.box) {
return orientedBoundingBoxToCartographicBounds(boundingVolume);
}
if (boundingVolumeHeader.region) {
const [west, south, east, north, minHeight, maxHeight] = boundingVolumeHeader.region;
return [
[(0, import_core4.degrees)(west), (0, import_core4.degrees)(south), minHeight],
[(0, import_core4.degrees)(east), (0, import_core4.degrees)(north), maxHeight]
];
}
if (boundingVolumeHeader.sphere) {
return boundingSphereToCartographicBounds(boundingVolume);
}
throw new Error("Unkown boundingVolume type");
}
function createBox(box, transform, result) {
const center = new import_core4.Vector3(box[0], box[1], box[2]);
transform.transform(center, center);
let origin = [];
if (box.length === 10) {
const halfSize = box.slice(3, 6);
const quaternion = new import_core4.Quaternion();
quaternion.fromArray(box, 6);
const x = new import_core4.Vector3([1, 0, 0]);
const y = new import_core4.Vector3([0, 1, 0]);
const z = new import_core4.Vector3([0, 0, 1]);
x.transformByQuaternion(quaternion);
x.scale(halfSize[0]);
y.transformByQuaternion(quaternion);
y.scale(halfSize[1]);
z.transformByQuaternion(quaternion);
z.scale(halfSize[2]);
origin = [...x.toArray(), ...y.toArray(), ...z.toArray()];
} else {
origin = [...box.slice(3, 6), ...box.slice(6, 9), ...box.slice(9, 12)];
}
const xAxis = transform.transformAsVector(origin.slice(0, 3));
const yAxis = transform.transformAsVector(origin.slice(3, 6));
const zAxis = transform.transformAsVector(origin.slice(6, 9));
const halfAxes = new import_core4.Matrix3([
xAxis[0],
xAxis[1],
xAxis[2],
yAxis[0],
yAxis[1],
yAxis[2],
zAxis[0],
zAxis[1],
zAxis[2]
]);
if (defined(result)) {
result.center = center;
result.halfAxes = halfAxes;
return result;
}
return new import_culling3.OrientedBoundingBox(center, halfAxes);
}
function createSphere(sphere, transform, result) {
const center = new import_core4.Vector3(sphere[0], sphere[1], sphere[2]);
transform.transform(center, center);
const scale = transform.getScale(scratchScale);
const uniformScale = Math.max(Math.max(scale[0], scale[1]), scale[2]);
const radius = sphere[3] * uniformScale;
if (defined(result)) {
result.center = center;
result.radius = radius;
return result;
}
return new import_culling3.BoundingSphere(center, radius);
}
function createObbFromRegion(region) {
const [west, south, east, north, minHeight, maxHeight] = region;
const northWest = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core4.degrees)(west), (0, import_core4.degrees)(north), minHeight], scratchNorthWest);
const southEast = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core4.degrees)(east), (0, import_core4.degrees)(south), maxHeight], scratchSouthEast);
const centerInCartesian = new import_core4.Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5);
import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter);
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core4.degrees)(east), scratchCenter[1], scratchCenter[2]], scratchXAxis);
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter[0], (0, import_core4.degrees)(north), scratchCenter[2]], scratchYAxis);
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter[0], scratchCenter[1], maxHeight], scratchZAxis);
return createBox([
...centerInCartesian,
...scratchXAxis.subtract(centerInCartesian),
...scratchYAxis.subtract(centerInCartesian),
...scratchZAxis.subtract(centerInCartesian)
], new import_core4.Matrix4());
}
function orientedBoundingBoxToCartographicBounds(boundingVolume) {
const result = emptyCartographicBounds();
const { halfAxes } = boundingVolume;
const xAxis = new import_core4.Vector3(halfAxes.getColumn(0));
const yAxis = new import_core4.Vector3(halfAxes.getColumn(1));
const zAxis = new import_core4.Vector3(halfAxes.getColumn(2));
for (let x = 0; x < 2; x++) {
for (let y = 0; y < 2; y++) {
for (let z = 0; z < 2; z++) {
scratchPoint.copy(boundingVolume.center);
scratchPoint.add(xAxis);
scratchPoint.add(yAxis);
scratchPoint.add(zAxis);
addToCartographicBounds(result, scratchPoint);
zAxis.negate();
}
yAxis.negate();
}
xAxis.negate();
}
return result;
}
function boundingSphereToCartographicBounds(boundingVolume) {
const result = emptyCartographicBounds();
const { center, radius } = boundingVolume;
const point = import_geospatial4.Ellipsoid.WGS84.scaleToGeodeticSurface(center, scratchPoint);
let zAxis;
if (point) {
zAxis = import_geospatial4.Ellipsoid.WGS84.geodeticSurfaceNormal(point);
} else {
zAxis = new import_core4.Vector3(0, 0, 1);
}
let xAxis = new import_core4.Vector3(zAxis[2], -zAxis[1], 0);
if (xAxis.len() > 0) {
xAxis.normalize();
} else {
xAxis = new import_core4.Vector3(0, 1, 0);
}
const yAxis = xAxis.clone().cross(zAxis);
for (const axis of [xAxis, yAxis, zAxis]) {
scratchScale.copy(axis).scale(radius);
for (let dir = 0; dir < 2; dir++) {
scratchPoint.copy(center);
scratchPoint.add(scratchScale);
addToCartographicBounds(result, scratchPoint);
scratchScale.negate();
}
}
return result;
}
function emptyCartographicBounds() {
return [
[Infinity, Infinity, Infinity],
[-Infinity, -Infinity, -Infinity]
];
}
function addToCartographicBounds(target, cartesian) {
import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(cartesian, scratchPoint);
target[0][0] = Math.min(target[0][0], scratchPoint[0]);
target[0][1] = Math.min(target[0][1], scratchPoint[1]);
target[0][2] = Math.min(target[0][2], scratchPoint[2]);
target[1][0] = Math.max(target[1][0], scratchPoint[0]);
target[1][1] = Math.max(target[1][1], scratchPoint[1]);
target[1][2] = Math.max(target[1][2], scratchPoint[2]);
}
// dist/tileset/helpers/tiles-3d-lod.js
var import_core5 = require("@math.gl/core");
var scratchPositionNormal = new import_core5.Vector3();
var scratchCartographic = new import_core5.Vector3();
var scratchMatrix = new import_core5.Matrix4();
var scratchCenter2 = new import_core5.Vector3();
var scratchPosition2 = new import_core5.Vector3();
var scratchDirection = new import_core5.Vector3();
function fog(distanceToCamera, density) {
const scalar = distanceToCamera * density;
return 1 - Math.exp(-(scalar * scalar));
}
function getDynamicScreenSpaceError(tileset, distanceToCamera) {
if (tileset.dynamicScreenSpaceError && tileset.dynamicScreenSpaceErrorComputedDensity) {
const density = tileset.dynamicScreenSpaceErrorComputedDensity;
const factor = tileset.dynamicScreenSpaceErrorFactor;
const dynamicError = fog(distanceToCamera, density) * factor;
return dynamicError;
}
return 0;
}
function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric) {
const tileset = tile.tileset;
const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
if (lodMetricValue === 0) {
return 0;
}
const distance = Math.max(tile._distanceToCamera, 1e-7);
const { height, sseDenominator } = frameState;
const { viewDistanceScale } = tileset.options;
let error = lodMetricValue * height * (viewDistanceScale || 1) / (distance * sseDenominator);
error -= getDynamicScreenSpaceError(tileset, distance);
return error;
}
// dist/tileset/helpers/i3s-lod.js
var import_core6 = require("@math.gl/core");
var import_geospatial5 = require("@math.gl/geospatial");
var cameraPositionCartesian = new import_core6.Vector3();
var toEye = new import_core6.Vector3();
var cameraPositionEnu = new import_core6.Vector3();
var extraVertexEnu = new import_core6.Vector3();
var projectedOriginVector = new import_core6.Vector3();
var enuToCartesianMatrix = new import_core6.Matrix4();
var cartesianToEnuMatrix = new import_core6.Matrix4();
function getLodStatus(tile, frameState) {
if (tile.lodMetricValue === 0 || isNaN(tile.lodMetricValue)) {
return "DIG";
}
const screenSize = 2 * getProjectedRadius(tile, frameState);
if (screenSize < 2) {
return "OUT";
}
if (!tile.header.children || screenSize <= tile.lodMetricValue) {
return "DRAW";
} else if (tile.header.children) {
return "DIG";
}
return "OUT";
}
function getProjectedRadius(tile, frameState) {
const { topDownViewport: viewport } = frameState;
const mbsLat = tile.header.mbs[1];
const mbsLon = tile.header.mbs[0];
const mbsZ = tile.header.mbs[2];
const mbsR = tile.header.mbs[3];
const mbsCenterCartesian = [...tile.boundingVolume.center];
const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
import_geospatial5.Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, cameraPositionCartesian);
toEye.copy(cameraPositionCartesian).subtract(mbsCenterCartesian).normalize();
import_geospatial5.Ellipsoid.WGS84.eastNorthUpToFixedFrame(mbsCenterCartesian, enuToCartesianMatrix);
cartesianToEnuMatrix.copy(enuToCartesianMatrix).invert();
cameraPositionEnu.copy(cameraPositionCartesian).transform(cartesianToEnuMatrix);
const projection = Math.sqrt(cameraPositionEnu[0] * cameraPositionEnu[0] + cameraPositionEnu[1] * cameraPositionEnu[1]);
const extraZ = projection * projection / cameraPositionEnu[2];
extraVertexEnu.copy([cameraPositionEnu[0], cameraPositionEnu[1], extraZ]);
const extraVertexCartesian = extraVertexEnu.transform(enuToCartesianMatrix);
const extraVectorCartesian = extraVertexCartesian.subtract(mbsCenterCartesian).normalize();
const radiusVector = toEye.cross(extraVectorCartesian).normalize().scale(mbsR);
const sphereMbsBorderVertexCartesian = radiusVector.add(mbsCenterCartesian);
const sphereMbsBorderVertexCartographic = import_geospatial5.Ellipsoid.WGS84.cartesianToCartographic(sphereMbsBorderVertexCartesian);
const projectedOrigin = viewport.project([mbsLon, mbsLat, mbsZ]);
const projectedMbsBorderVertex = viewport.project(sphereMbsBorderVertexCartographic);
const projectedRadius = projectedOriginVector.copy(projectedOrigin).subtract(projectedMbsBorderVertex).magnitude();
return projectedRadius;
}
// dist/tileset/helpers/3d-tiles-options.js
function get3dTilesOptions(tileset) {
return {
assetGltfUpAxis: tileset.asset && tileset.asset.gltfUpAxis || "Y"
};
}
// dist/utils/managed-array.js
var import_loader_utils3 = require("@loaders.gl/loader-utils");
var ManagedArray = class {
_map = /* @__PURE__ */ new Map();
_array;
_length;
constructor(length = 0) {
this._array = new Array(length);
this._length = length;
}
/**
* Gets or sets the length of the array.
* If the set length is greater than the length of the internal array, the internal array is resized.
*
* @memberof ManagedArray.prototype
* @type Number
*/
get length() {
return this._length;
}
set length(length) {
this._length = length;
if (length > this._array.length) {
this._array.length = length;
}
}
/**
* Gets the internal array.
*
* @memberof ManagedArray.prototype
* @type Array
* @readonly
*/
get values() {
return this._array;
}
/**
* Gets the element at an index.
*
* @param {Number} index The index to get.
*/
get(index) {
(0, import_loader_utils3.assert)(index < this._array.length);
return this._array[index];
}
/**
* Sets the element at an index. Resizes the array if index is greater than the length of the array.
*
* @param {Number} index The index to set.
* @param {*} element The element to set at index.
*/
set(index, element) {
(0, import_loader_utils3.assert)(index >= 0);
if (index >= this.length) {
this.length = index + 1;
}
if (this._map.has(this._array[index])) {
this._map.delete(this._array[index]);
}
this._array[index] = element;
this._map.set(element, index);
}
delete(element) {
const index = this._map.get(element);
if (index >= 0) {
this._array.splice(index, 1);
this._map.delete(element);
this.length--;
}
}
/**
* Returns the last element in the array without modifying the array.
*
* @returns {*} The last element in the array.
*/
peek() {
return this._array[this._length - 1];
}
/**
* Push an element into the array.
*
* @param {*} element The element to push.
*/
push(element) {
if (!this._map.has(element)) {
const index = this.length++;
this._array[index] = element;
this._map.set(element, index);
}
}
/**
* Pop an element from the array.
*
* @returns {*} The last element in the array.
*/
pop() {
const element = this._array[--this.length];
this._map.delete(element);
return element;
}
/**
* Resize the internal array if length > _array.length.
*
* @param {Number} length The length.
*/
reserve(length) {
(0, import_loader_utils3.assert)(length >= 0);
if (length > this._array.length) {
this._array.length = length;
}
}
/**
* Resize the array.
*
* @param {Number} length The length.
*/
resize(length) {
(0, import_loader_utils3.assert)(length >= 0);
this.length = length;
}
/**
* Trim the internal array to the specified length. Defaults to the current length.
*
* @param {Number} [length] The length.
*/
trim(length) {
if (length === null || length === void 0) {
length = this.length;
}
this._array.length = length;
}
reset() {
this._array = [];
this._map = /* @__PURE__ */ new Map();
this._length = 0;
}
find(target) {
return this._map.has(target);
}
};
// dist/tileset/tileset-traverser.js
var DEFAULT_PROPS = {
loadSiblings: false,
skipLevelOfDetail: false,
updateTransforms: true,
onTraversalEnd: () => {
},
viewportTraversersMap: {},
basePath: ""
};
var TilesetTraverser = class {
options;
// fulfill in traverse call
root = null;
// tiles should be rendered
selectedTiles = {};
// tiles should be loaded from server
requestedTiles = {};
// tiles does not have render content
emptyTiles = {};
lastUpdate = new Date().getTime();
updateDebounceTime = 1e3;
/** temporary storage to hold the traversed tiles during a traversal */
_traversalStack = new ManagedArray();
_emptyTraversalStack = new ManagedArray();
/** set in every traverse cycle */
_frameNumber = null;
// RESULT
traversalFinished(frameState) {
return true;
}
// TODO nested props
constructor(options) {
this.options = { ...DEFAULT_PROPS, ...options };
}
// tiles should be visible
traverse(root, frameState, options) {
this.root = root;
this.options = { ...this.options, ...options };
this.reset();
this.updateTile(root, frameState);
this._frameNumber = frameState.frameNumber;
this.executeTraversal(root, frameState);
}
reset() {
this.requestedTiles = {};
this.selectedTiles = {};
this.emptyTiles = {};
this._traversalStack.reset();
this._emptyTraversalStack.reset();
}
/**
* Execute traverse
* Depth-first traversal that traverses all visible tiles and marks tiles for selection.
* If skipLevelOfDetail is off then a tile does not refine until all children are loaded.
* This is the traditional replacement refinement approach and is called the base traversal.
* Tiles that have a greater screen space error than the base screen space error are part of the base traversal,
* all other tiles are part of the skip traversal. The skip traversal allows for skipping levels of the tree
* and rendering children and parent tiles simultaneously.
*/
/* eslint-disable-next-line complexity, max-statements */
executeTraversal(root, frameState) {
const stack = this._traversalStack;
root._selectionDepth = 1;
stack.push(root);
while (stack.length > 0) {
const tile = stack.pop();
let shouldRefine = false;
if (this.canTraverse(tile, frameState)) {
this.updateChildTiles(tile, frameState);
shouldRefine = this.updateAndPushChildren(tile, frameState, stack, tile.hasRenderContent ? tile._selectionDepth + 1 : tile._selectionDepth);
}
const parent = tile.parent;
const parentRefines = Boolean(!parent || parent._shouldRefine);
const stoppedRefining = !shouldRefine;
if (!tile.hasRenderContent) {
this.emptyTiles[tile.id] = tile;
this.loadTile(tile, frameState);
if (stoppedRefining) {
this.selectTile(tile, frameState);
}
} else if (tile.refine === TILE_REFINEMENT.ADD) {
this.loadTile(tile, frameState);
this.selectTile(tile, frameState);
} else if (tile.refine === TILE_REFINEMENT.REPLACE) {
this.loadTile(tile, frameState);
if (stoppedRefining) {
this.selectTile(tile, frameState);
}
}
this.touchTile(tile, frameState);
tile._shouldRefine = shouldRefine && parentRefines;
}
const newTime = new Date().getTime();
if (this.traversalFinished(frameState) || newTime - this.lastUpdate > this.updateDebounceTime) {
this.lastUpdate = newTime;
this.options.onTraversalEnd(frameState);
}
}
updateChildTiles(tile, frameState) {
const children = tile.children;
for (const child of children) {
this.updateTile(child, frameState);
}
}
/* eslint-disable complexity, max-statements */
updateAndPushChildren(tile, frameState, stack, depth) {
const { loadSiblings, skipLevelOfDetail } = this.options;
const children = tile.children;
children.sort(this.compareDistanceToCamera.bind(this));
const checkRefines = tile.refine === TILE_REFINEMENT.REPLACE && tile.hasRenderContent && !skipLevelOfDetail;
let hasVisibleChild = false;
let refines = true;
for (const child of children) {
child._selectionDepth = depth;
if (child.isVisibleAndInRequestVolume) {
if (stack.find(child)) {
stack.delete(child);
}
stack.push(child);
hasVisibleChild = true;
} else if (checkRefines || loadSiblings) {
this.loadTile(child, frameState);
this.touchTile(child, frameState);
}
if (checkRefines) {
let childRefines;
if (!child._inRequestVolume) {
childRefines = false;
} else if (!child.hasRenderContent) {
childRefines = this.executeEmptyTraversal(child, frameState);
} else {
childRefines = child.contentAvailable;
}
refines = refines && childRefines;
if (!refines) {
return false;
}
}
}
if (!hasVisibleChild) {
refines = false;
}
return refines;
}
/* eslint-enable complexity, max-statements */
updateTile(tile, frameState) {
this.updateTileVisibility(tile, frameState);
}
// tile to render in the browser
selectTile(tile, frameState) {
if (this.shouldSelectTile(tile)) {
tile._selectedFrame = frameState.frameNumber;
this.selectedTiles[tile.id] = tile;
}
}
// tile to load from server
loadTile(tile, frameState) {
if (this.shouldLoadTile(tile)) {
tile._requestedFrame = frameState.frameNumber;
tile._priority = tile._getPriority();
this.requestedTiles[tile.id] = tile;
}
}
// cache tile
touchTile(tile, frameState) {
tile.tileset._cache.touch(tile);
tile._touchedFrame = frameState.frameNumber;
}
// tile should be visible
// tile should have children
// tile LoD (level of detail) is not sufficient under current viewport
canTraverse(tile, frameState) {
if (!tile.hasChildren) {
return false;
}
if (tile.hasTilesetContent) {
return !tile.contentExpired;
}
return this.shouldRefine(tile, frameState);
}
shouldLoadTile(tile) {
return tile.hasUnloadedContent || tile.contentExpired;
}
shouldSelectTile(tile) {
return tile.contentAvailable && !this.options.skipLevelOfDetail;
}
/** Decide if tile LoD (level of detail) is not sufficient under current viewport */
shouldRefine(tile, frameState, useParentMetric = false) {
let screenSpaceError = tile._screenSpaceError;
if (useParentMetric) {
screenSpaceError = tile.getScreenSpaceError(frameState, true);
}
return screenSpaceError > tile.tileset.memoryAdjustedScreenSpaceError;
}
updateTileVisibility(tile, frameState) {
const viewportIds = [];
if (this.options.viewportTraversersMap) {
for (const key in this.options.viewportTraversersMap) {
const value = this.options.viewportTraversersMap[key];
if (value === frameState.viewport.id) {
viewportIds.push(key);
}
}
} else {
viewportIds.push(frameState.viewport.id);
}
tile.updateVisibility(frameState, viewportIds);
}
// UTILITIES
compareDistanceToCamera(b, a) {
return b._distanceToCamera - a._distanceToCamera;
}
anyChildrenVisible(tile, frameState) {
let anyVisible = false;
for (const child of tile.children) {
child.updateVisibility(frameState);
anyVisible = anyVisible || child.isVisibleAndInRequestVolume;
}
return anyVisible;
}
// Depth-first traversal that checks if all nearest descendants with content are loaded.
// Ignores visibility.
executeEmptyTraversal(root, frameState) {
let allDescendantsLoaded = true;
const stack = this._emptyTraversalStack;
stack.push(root);
while (stack.length > 0) {
const tile = stack.pop();
const traverse = !tile.hasRenderContent && this.canTraverse(tile, frameState);
const emptyLeaf = !tile.hasRenderContent && tile.children.length === 0;
if (!traverse && !tile.contentAvailable && !emptyLeaf) {
allDescendantsLoaded = false;
}
this.updateTile(tile, frameState);
if (!tile.isVisibleAndInRequestVolume) {
this.loadTile(tile, frameState);
this.touchTile(tile, frameState);
}
if (traverse) {
const children = tile.children;
for (const child of children) {
stack.push(child);
}
}
}
return allDescendantsLoaded;
}
};
// dist/tileset/tile-3d.js
var scratchVector3 = new import_core7.Vector3();
function defined2(x) {
return x !== void 0 && x !== null;
}
var Tile3D = class {
tileset;
header;
id;
url;
parent;
/* Specifies the type of refine that is used when traversing this tile for rendering. */
refine;
type;
contentUrl;
/** Different refinement algorithms used by I3S and 3D tiles */
lodMetricType = "geometricError";
/** The error, in meters, introduced if this tile is rendered and its children are not. */
lodMetricValue = 0;
/** @todo math.gl is not exporting BoundingVolume base type? */
boundingVolume = null;
/**
* The tile's content. This represents the actual tile's payload,
* not the content's metadata in the tileset JSON file.
*/
content = null;
contentState = TILE_CONTENT_STATE.UNLOADED;
gpuMemoryUsageInBytes = 0;
/** The tile's children - an array of Tile3D objects. */
children = [];
depth = 0;
viewportIds = [];
transform = new import_core7.Matrix4();
extensions = null;
/** TODO Cesium 3d tiles specific */
implicitTiling = null;
/** Container to store application specific data */
userData = {};
computedTransform;
hasEmptyContent = false;
hasTilesetContent = false;
traverser = new TilesetTraverser({});
/** Used by TilesetCache */
_cacheNode = null;
_frameNumber = null;
// TODO Cesium 3d tiles specific
_expireDate = null;
_expiredContent = null;
_boundingBox = void 0;
/** updated every frame for tree traversal and rendering optimizations: */
_distanceToCamera = 0;
_screenSpaceError = 0;
_visibilityPlaneMask;
_visible = void 0;
_contentBoundingVolume;
_viewerRequestVolume;
_initialTransform = new import_core7.Matrix4();
// Used by traverser, cannot be marked private
_priority = 0;
_selectedFrame = 0;
_requestedFrame = 0;
_selectionDepth = 0;
_touchedFrame = 0;
_centerZDepth = 0;
_shouldRefine = false;
_stackLength = 0;
_visitedFrame = 0;
_inRequestVolume = false;
_lodJudge = null;
// TODO i3s specific, needs to remove
/**
* @constructs
* Create a Tile3D instance
* @param tileset - Tileset3D instance
* @param header - tile header - JSON loaded from a dataset
* @param parentHeader - parent Tile3D instance
* @param extendedId - optional ID to separate copies of a tile for different viewports.
* const extendedId = `${tile.id}-${frameState.viewport.id}`;
*/
// eslint-disable-next-line max-statements
constructor(tileset, header, parentHeader, extendedId = "") {
this.header = header;
this.tileset = tileset;
this.id = extendedId || header.id;
this.url = header.url;
this.parent = parentHeader;
this.refine = this._getRefine(header.refine);
this.type = header.type;
this.contentUrl = header.contentUrl;
this._initializeLodMetric(header);
this._initializeTransforms(header);
this._initializeBoundingVolumes(header);
this._initializeContent(header);
this._initializeRenderingState(header);
Object.seal(this);
}
destroy() {
this.header = null;
}
isDestroyed() {
return this.header === null;
}
get selected() {
return this._selectedFrame === this.tileset._frameNumber;
}
get isVisible() {
return this._visible;
}
get isVisibleAndInRequestVolume() {
return this._visible && this._inRequestVolume;
}
/** Returns true if tile is not an empty tile and not an external tileset */
get hasRenderContent() {
return !this.hasEmptyContent && !this.hasTilesetContent;
}
/** Returns true if tile has children */
get hasChildren() {
return this.children.length > 0 || this.header.children && this.header.children.length > 0;
}
/**
* Determines if the tile's content is ready. This is automatically `true` for
* tiles with empty content.
*/
get contentReady() {
return this.contentState === TILE_CONTENT_STATE.READY || this.hasEmptyContent;
}
/**
* Determines if the tile has available content to render. `true` if the tile's
* content is ready or if it has expired content this renders while new content loads; otherwise,
*/
get contentAvailable() {
return Boolean(this.contentReady && this.hasRenderContent || this._expiredContent && !this.contentFailed);
}
/** Returns true if tile has renderable content but it's unloaded */
get hasUnloadedContent() {
return this.hasRenderContent && this.contentUnloaded;
}
/**
* Determines if the tile's content has not be requested. `true` if tile's
* content has not be requested; otherwise, `false`.
*/
get contentUnloaded() {
return this.contentState === TILE_CONTENT_STATE.UNLOADED;
}
/**
* Determines if the tile's content is expired. `true` if tile's
* content is expired; otherwise, `false`.
*/
get contentExpired() {
return this.contentState === TILE_CONTENT_STATE.EXPIRED;
}
// Determines if the tile's content failed to load. `true` if the tile's
// content failed to load; otherwise, `false`.
get contentFailed() {
return this.contentState === TILE_CONTENT_STATE.FAILED;
}
/**
* Distance from the tile's bounding volume center to the camera
*/
get distanceToCamera() {
return this._distanceToCamera;
}
/**
* Screen space error for LOD selection
*/
get screenSpaceError() {
return this._screenSpaceError;
}
/**
* Get bounding box in cartographic coordinates
* @returns [min, max] each in [longitude, latitude, altitude]
*/
get boundingBox() {
if (!this._boundingBox) {
this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume);
}
return this._boundingBox;
}
/** Get the tile's screen space error. */
getScreenSpaceError(frameState, useParentLodMetric) {
switch (this.tileset.type) {
case TILESET_TYPE.I3S:
return getProjectedRadius(this, frameState);
case TILESET_TYPE.TILES3D:
return getTiles3DScreenSpaceError(this, frameState, useParentLodMetric);
default:
throw new Error("Unsupported tileset type");
}
}
/**
* Make tile unselected than means it won't be shown
* but it can be still loaded in memory
*/
unselect() {
this._selectedFrame = 0;
}
/**
* Memory usage of tile on GPU
*/
_getGpuMemoryUsageInBytes() {
return this.content.gpuMemoryUsageInBytes || this.content.byteLength || 0;
}
/*
* If skipLevelOfDetail is off try to load child tiles as soon as possible so that their parent can refine sooner.
* Tiles are prioritized by screen space error.
*/
// eslint-disable-next-line complexity
_getPriority() {
const traverser = this.tileset._traverser;
const { skipLevelOfDetail } = traverser.options;
const maySkipTile = this.refine === TILE_REFINEMENT.ADD || skipLevelOfDetail;
if (maySkipTile && !this.isVisible && this._visible !== void 0) {
return -1;
}
if (this.tileset._frameNumber - this._touchedFrame >= 1) {
return -1;
}
if (this.contentState === TILE_CONTENT_STATE.UNLOADED) {
return -1;
}
const parent = this.parent;
const useParentScreenSpaceError = parent && (!maySkipTile || this._screenSpaceError === 0 || parent.hasTilesetContent);
const screenSpaceError = useParentScreenSpaceError ? parent._screenSpaceError : this._screenSpaceError;
const rootScreenSpaceError = traverser.root ? traverser.root._screenSpaceError : 0;
return Math.max(rootScreenSpaceError - screenSpaceError, 0);
}
/**
* Requests the tile's content.
* The request may not be made if the Request Scheduler can't prioritize it.
*/
// eslint-disable-next-line max-statements, complexity
async loadContent() {
if (this.hasEmptyContent) {
return false;
}
if (this.content) {
return true;
}
const expired = this.contentExpired;
if (expired) {
this._expireDate = null;
}
this.contentState = TILE_CONTENT_STATE.LOADING;
const requestToken = await this.tileset._requestScheduler.scheduleRequest(this.id, this._getPriority.bind(this));
if (!requestToken) {
this.contentState = TILE_CONTENT_STATE.UNLOADED;
return false;
}
try {
const contentUrl = this.tileset.getTileUrl(this.contentUrl);
const loader = this.tileset.loader;
const options = {
...this.tileset.loadOptions,
[loader.id]: {
// @ts-expect-error
...this.tileset.loadOptions[loader.id],
isTileset: this.type === "json",
...this._getLoaderSpecificOptions(loader.id)
}
};
this.content = await (0, import_core8.load)(contentUrl, loader, options);
if (this.tileset.options.contentLoader) {
await this.tileset.options.contentLoader(this);
}
if (this._isTileset()) {
this.tileset._initializeTileHeaders(this.content, this);
}
this.contentState = TILE_CONTENT_STATE.READY;
this._onContentLoaded();
return true;
} catch (error) {
this.contentState = TILE_CONTENT_STATE.FAILED;
throw error;
} finally {
requestToken.done();
}
}
// Unloads the tile's content.
unloadContent() {
if (this.content && this.content.destroy) {
this.content.destroy();
}
this.content = null;
if (this.header.content && this.header.content.destroy) {
this.header.content.destroy();
}
this.header.content = null;
this.contentState = TILE_CONTENT_STATE.UNLOADED;
return true;
}
/**
* Update the tile's visibility
* @param {Object} frameState - frame state for tile culling
* @param {string[]} viewportIds - a list of viewport ids that show this tile
* @return {void}
*/
updateVisibility(frameState, viewportIds) {
if (this._frameNumber === frameState.frameNumber) {
return;
}
const parent = this.parent;
const parentVisibilityPlaneMask = parent ? parent._visibilityPlaneMask : import_culling4.CullingVolume.MASK_INDETERMINATE;
if (this.tileset._traverser.options.updateTransforms) {
const parentTransform = parent ? parent.computedTransform : this.tileset.modelMatrix;
this._updateTransform(parentTransform);
}
this._distanceToCamera = this.distanceToTile(frameState);
this._screenSpaceError = this.getScreenSpaceError(frameState, false);
this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask);
this._visible = this._visibilityPlaneMask !== import_culling4.CullingVolume.MASK_OUTSIDE;
this._inRequestVolume = this.insideViewerRequestVolume(frameState);
this._frameNumber = frameState.frameNumber;
this.viewportIds = viewportIds;
}
// Determines whether the tile's bounding volume intersects the culling volume.
// @param {FrameState} frameState The frame state.
// @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check.
// @returns {Number} A plane mask as described above in {@link CullingVolume#computeVisibilityWit