@loaders.gl/tiles 5.0.0-alpha.1 → 5.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +14 -3
- package/dist/dist.dev.js +2396 -274
- package/dist/dist.min.js +1 -1
- package/dist/index.cjs +2763 -337
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
- package/dist/point-cloud/point-cloud-tile.js +3 -0
- package/dist/point-cloud/point-cloud-tile.js.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.js +3 -0
- package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
- package/dist/point-cloud/types.d.ts.map +1 -1
- package/dist/point-cloud/types.js +3 -0
- package/dist/point-cloud/types.js.map +1 -1
- package/dist/raster-set.d.ts +35 -29
- package/dist/raster-set.d.ts.map +1 -1
- package/dist/raster-set.js +7 -1
- package/dist/raster-set.js.map +1 -1
- package/dist/tileset-3d/common/tile-3d.d.ts +98 -3
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +229 -15
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +143 -3
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +257 -16
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
- package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-cache.js +11 -3
- package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
- package/dist/tileset-3d/common/tileset-source.d.ts +18 -0
- package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-source.js.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.d.ts +13 -0
- package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.js +28 -1
- package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +111 -0
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +340 -0
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +92 -3
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +211 -13
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +36 -0
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +66 -0
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
- package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
- package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +3 -0
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
- package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.js +1 -0
- package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts +59 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +84 -9
- package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
- package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
- package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
- package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/doubly-linked-list.d.ts.map +1 -1
- package/dist/utils/doubly-linked-list.js +1 -0
- package/dist/utils/doubly-linked-list.js.map +1 -1
- package/dist/utils/managed-array.d.ts.map +1 -1
- package/dist/utils/managed-array.js +1 -0
- package/dist/utils/managed-array.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +19 -0
- package/src/point-cloud/point-cloud-tile.ts +4 -0
- package/src/point-cloud/point-cloud-tileset.ts +4 -0
- package/src/point-cloud/types.ts +4 -0
- package/src/raster-set.ts +81 -43
- package/src/tileset-3d/common/tile-3d.ts +285 -21
- package/src/tileset-3d/common/tileset-3d.ts +368 -17
- package/src/tileset-3d/common/tileset-cache.ts +15 -7
- package/src/tileset-3d/common/tileset-source.ts +21 -0
- package/src/tileset-3d/common/tileset-traverser.ts +29 -1
- package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +534 -0
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +266 -13
- package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +77 -0
- package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
- package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
- package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +4 -0
- package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
- package/src/tileset-3d/helpers/bounding-volume.ts +2 -0
- package/src/tileset-3d/helpers/frame-state.ts +139 -11
- package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
- package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
- package/src/types.ts +6 -0
- package/src/utils/doubly-linked-list.ts +2 -0
- package/src/utils/managed-array.ts +2 -0
package/dist/dist.dev.js
CHANGED
|
@@ -13,7 +13,11 @@ var __exports__ = (() => {
|
|
|
13
13
|
var __getProtoOf = Object.getPrototypeOf;
|
|
14
14
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
15
15
|
var __commonJS = (cb, mod) => function __require() {
|
|
16
|
-
|
|
16
|
+
try {
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw mod = 0, e;
|
|
20
|
+
}
|
|
17
21
|
};
|
|
18
22
|
var __export = (target, all) => {
|
|
19
23
|
for (var name in all)
|
|
@@ -71,9 +75,12 @@ var __exports__ = (() => {
|
|
|
71
75
|
TilesetTraverser: () => TilesetTraverser,
|
|
72
76
|
calculateTransformProps: () => calculateTransformProps,
|
|
73
77
|
createBoundingVolume: () => createBoundingVolume,
|
|
78
|
+
createImplicitSubtreeReference: () => createImplicitSubtreeReference,
|
|
74
79
|
getFrameState: () => getFrameState,
|
|
75
80
|
getLodStatus: () => getLodStatus,
|
|
76
|
-
isTileset3DSource: () => isTileset3DSource
|
|
81
|
+
isTileset3DSource: () => isTileset3DSource,
|
|
82
|
+
materializeImplicitSubtree: () => materializeImplicitSubtree,
|
|
83
|
+
replaceImplicitUrlTemplate: () => replaceImplicitUrlTemplate
|
|
77
84
|
});
|
|
78
85
|
__reExport(bundle_exports, __toESM(require_core(), 1));
|
|
79
86
|
|
|
@@ -110,6 +117,9 @@ var __exports__ = (() => {
|
|
|
110
117
|
function degrees(radians2, result) {
|
|
111
118
|
return map(radians2, (radians3) => radians3 * RADIANS_TO_DEGREES, result);
|
|
112
119
|
}
|
|
120
|
+
function clamp(value, min2, max2) {
|
|
121
|
+
return map(value, (value2) => Math.max(min2, Math.min(max2, value2)));
|
|
122
|
+
}
|
|
113
123
|
function equals(a, b, epsilon) {
|
|
114
124
|
const oldEpsilon = config.EPSILON;
|
|
115
125
|
if (epsilon) {
|
|
@@ -569,7 +579,7 @@ var __exports__ = (() => {
|
|
|
569
579
|
out[1] = m[1] * x + m[5] * y + m[13];
|
|
570
580
|
return out;
|
|
571
581
|
}
|
|
572
|
-
var forEach = function() {
|
|
582
|
+
var forEach = (function() {
|
|
573
583
|
const vec = create();
|
|
574
584
|
return function(a, stride, offset, count, fn, arg) {
|
|
575
585
|
let i;
|
|
@@ -594,7 +604,7 @@ var __exports__ = (() => {
|
|
|
594
604
|
}
|
|
595
605
|
return a;
|
|
596
606
|
};
|
|
597
|
-
}();
|
|
607
|
+
})();
|
|
598
608
|
|
|
599
609
|
// ../../node_modules/@math.gl/core/dist/lib/gl-matrix-extras.js
|
|
600
610
|
function vec2_transformMat4AsVector(out, a, m) {
|
|
@@ -1041,7 +1051,7 @@ var __exports__ = (() => {
|
|
|
1041
1051
|
var sqrDist = squaredDistance;
|
|
1042
1052
|
var len = length;
|
|
1043
1053
|
var sqrLen = squaredLength;
|
|
1044
|
-
var forEach2 = function() {
|
|
1054
|
+
var forEach2 = (function() {
|
|
1045
1055
|
const vec = create2();
|
|
1046
1056
|
return function(a, stride, offset, count, fn, arg) {
|
|
1047
1057
|
let i;
|
|
@@ -1068,15 +1078,15 @@ var __exports__ = (() => {
|
|
|
1068
1078
|
}
|
|
1069
1079
|
return a;
|
|
1070
1080
|
};
|
|
1071
|
-
}();
|
|
1081
|
+
})();
|
|
1072
1082
|
|
|
1073
1083
|
// ../../node_modules/@math.gl/core/dist/classes/vector3.js
|
|
1074
1084
|
var ORIGIN = [0, 0, 0];
|
|
1075
1085
|
var ZERO;
|
|
1076
|
-
var Vector3 = class extends Vector {
|
|
1086
|
+
var Vector3 = class _Vector3 extends Vector {
|
|
1077
1087
|
static get ZERO() {
|
|
1078
1088
|
if (!ZERO) {
|
|
1079
|
-
ZERO = new
|
|
1089
|
+
ZERO = new _Vector3(0, 0, 0);
|
|
1080
1090
|
Object.freeze(ZERO);
|
|
1081
1091
|
}
|
|
1082
1092
|
return ZERO;
|
|
@@ -1193,10 +1203,10 @@ var __exports__ = (() => {
|
|
|
1193
1203
|
|
|
1194
1204
|
// ../../node_modules/@math.gl/core/dist/classes/vector4.js
|
|
1195
1205
|
var ZERO2;
|
|
1196
|
-
var Vector4 = class extends Vector {
|
|
1206
|
+
var Vector4 = class _Vector4 extends Vector {
|
|
1197
1207
|
static get ZERO() {
|
|
1198
1208
|
if (!ZERO2) {
|
|
1199
|
-
ZERO2 = new
|
|
1209
|
+
ZERO2 = new _Vector4(0, 0, 0, 0);
|
|
1200
1210
|
Object.freeze(ZERO2);
|
|
1201
1211
|
}
|
|
1202
1212
|
return ZERO2;
|
|
@@ -3310,7 +3320,7 @@ var __exports__ = (() => {
|
|
|
3310
3320
|
out[3] = a[3];
|
|
3311
3321
|
return out;
|
|
3312
3322
|
}
|
|
3313
|
-
var forEach3 = function() {
|
|
3323
|
+
var forEach3 = (function() {
|
|
3314
3324
|
const vec = create5();
|
|
3315
3325
|
return function(a, stride, offset, count, fn, arg) {
|
|
3316
3326
|
let i;
|
|
@@ -3339,7 +3349,7 @@ var __exports__ = (() => {
|
|
|
3339
3349
|
}
|
|
3340
3350
|
return a;
|
|
3341
3351
|
};
|
|
3342
|
-
}();
|
|
3352
|
+
})();
|
|
3343
3353
|
|
|
3344
3354
|
// ../../node_modules/@math.gl/core/dist/classes/matrix4.js
|
|
3345
3355
|
var INDICES2;
|
|
@@ -4045,7 +4055,7 @@ var __exports__ = (() => {
|
|
|
4045
4055
|
var length3 = length2;
|
|
4046
4056
|
var squaredLength3 = squaredLength2;
|
|
4047
4057
|
var normalize3 = normalize2;
|
|
4048
|
-
var rotationTo = function() {
|
|
4058
|
+
var rotationTo = (function() {
|
|
4049
4059
|
const tmpvec3 = create2();
|
|
4050
4060
|
const xUnitVec3 = fromValues(1, 0, 0);
|
|
4051
4061
|
const yUnitVec3 = fromValues(0, 1, 0);
|
|
@@ -4072,8 +4082,8 @@ var __exports__ = (() => {
|
|
|
4072
4082
|
out[3] = 1 + dot4;
|
|
4073
4083
|
return normalize3(out, out);
|
|
4074
4084
|
};
|
|
4075
|
-
}();
|
|
4076
|
-
var sqlerp = function() {
|
|
4085
|
+
})();
|
|
4086
|
+
var sqlerp = (function() {
|
|
4077
4087
|
const temp1 = create6();
|
|
4078
4088
|
const temp2 = create6();
|
|
4079
4089
|
return function(out, a, b, c, d, t) {
|
|
@@ -4082,8 +4092,8 @@ var __exports__ = (() => {
|
|
|
4082
4092
|
slerp2(out, temp1, temp2, 2 * t * (1 - t));
|
|
4083
4093
|
return out;
|
|
4084
4094
|
};
|
|
4085
|
-
}();
|
|
4086
|
-
var setAxes = function() {
|
|
4095
|
+
})();
|
|
4096
|
+
var setAxes = (function() {
|
|
4087
4097
|
const matr = create3();
|
|
4088
4098
|
return function(out, view, right, up) {
|
|
4089
4099
|
matr[0] = right[0];
|
|
@@ -4097,7 +4107,7 @@ var __exports__ = (() => {
|
|
|
4097
4107
|
matr[8] = -view[2];
|
|
4098
4108
|
return normalize3(out, fromMat3(out, matr));
|
|
4099
4109
|
};
|
|
4100
|
-
}();
|
|
4110
|
+
})();
|
|
4101
4111
|
|
|
4102
4112
|
// ../../node_modules/@math.gl/core/dist/classes/quaternion.js
|
|
4103
4113
|
var IDENTITY_QUATERNION = [0, 0, 0, 1];
|
|
@@ -5106,6 +5116,506 @@ var __exports__ = (() => {
|
|
|
5106
5116
|
}
|
|
5107
5117
|
};
|
|
5108
5118
|
|
|
5119
|
+
// ../loader-utils/src/lib/request-utils/range-request-scheduler.ts
|
|
5120
|
+
var RANGE_STATS_KEYS = {
|
|
5121
|
+
logicalRanges: "Logical Range Requests",
|
|
5122
|
+
rangeBatches: "Range Request Batches",
|
|
5123
|
+
transportRanges: "Transport Ranges Created",
|
|
5124
|
+
completedTransportRanges: "Range Transport Requests Completed",
|
|
5125
|
+
coalescedRanges: "Coalesced Logical Ranges",
|
|
5126
|
+
requestedBytes: "Logical Range Request Bytes",
|
|
5127
|
+
transportBytes: "Range Transport Bytes Requested",
|
|
5128
|
+
responseBytes: "Range Response Bytes",
|
|
5129
|
+
networkTimeMs: "Range Network Time",
|
|
5130
|
+
overfetchBytes: "Range Overfetch Bytes",
|
|
5131
|
+
failedTransportRanges: "Range Transport Requests Failed",
|
|
5132
|
+
abortedLogicalRanges: "Aborted Logical Range Requests",
|
|
5133
|
+
fullResponseFallbacks: "Full Response Fallbacks"
|
|
5134
|
+
};
|
|
5135
|
+
var DEFAULT_PROPS2 = {
|
|
5136
|
+
batchDelayMs: 50,
|
|
5137
|
+
maxGapBytes: 65536,
|
|
5138
|
+
rangeExpansionBytes: 65536,
|
|
5139
|
+
maxMergedBytes: 8388608,
|
|
5140
|
+
stats: createRangeStats("range-request-scheduler-default"),
|
|
5141
|
+
onEvent: () => {
|
|
5142
|
+
}
|
|
5143
|
+
};
|
|
5144
|
+
var RangeRequestScheduler = class {
|
|
5145
|
+
/** Runtime scheduler configuration with defaults filled in. */
|
|
5146
|
+
props;
|
|
5147
|
+
/** Runtime counters for range batching and transport behavior. */
|
|
5148
|
+
stats;
|
|
5149
|
+
/** Optional event callback for range batching diagnostics. */
|
|
5150
|
+
onEvent;
|
|
5151
|
+
pendingRequests = [];
|
|
5152
|
+
flushTimer = null;
|
|
5153
|
+
/** Creates a scheduler for one group of byte-range-addressable resources. */
|
|
5154
|
+
constructor(props = {}) {
|
|
5155
|
+
const rangeExpansionBytes = props.rangeExpansionBytes ?? props.maxGapBytes ?? DEFAULT_PROPS2.rangeExpansionBytes;
|
|
5156
|
+
this.props = {
|
|
5157
|
+
batchDelayMs: props.batchDelayMs ?? DEFAULT_PROPS2.batchDelayMs,
|
|
5158
|
+
maxGapBytes: props.maxGapBytes ?? rangeExpansionBytes,
|
|
5159
|
+
rangeExpansionBytes,
|
|
5160
|
+
maxMergedBytes: props.maxMergedBytes ?? DEFAULT_PROPS2.maxMergedBytes
|
|
5161
|
+
};
|
|
5162
|
+
this.stats = props.stats || createRangeStats("range-request-scheduler");
|
|
5163
|
+
this.onEvent = props.onEvent;
|
|
5164
|
+
initializeStats(this.stats);
|
|
5165
|
+
}
|
|
5166
|
+
/**
|
|
5167
|
+
* Enqueues one byte range request and resolves with the exact requested byte slice.
|
|
5168
|
+
*/
|
|
5169
|
+
scheduleRequest(request) {
|
|
5170
|
+
if (request.length < 0) {
|
|
5171
|
+
return Promise.reject(new Error("Byte range length cannot be negative"));
|
|
5172
|
+
}
|
|
5173
|
+
if (request.signal?.aborted) {
|
|
5174
|
+
return Promise.reject(createAbortError());
|
|
5175
|
+
}
|
|
5176
|
+
if (request.length === 0) {
|
|
5177
|
+
return Promise.resolve(new ArrayBuffer(0));
|
|
5178
|
+
}
|
|
5179
|
+
const promise = new Promise((resolve2, reject) => {
|
|
5180
|
+
const pendingRequest = { ...request, resolve: resolve2, reject, settled: false };
|
|
5181
|
+
if (request.signal) {
|
|
5182
|
+
pendingRequest.abortListener = () => {
|
|
5183
|
+
this.abortRequest(pendingRequest);
|
|
5184
|
+
pendingRequest.onAbort?.();
|
|
5185
|
+
};
|
|
5186
|
+
request.signal.addEventListener("abort", pendingRequest.abortListener, { once: true });
|
|
5187
|
+
}
|
|
5188
|
+
this.pendingRequests.push(pendingRequest);
|
|
5189
|
+
});
|
|
5190
|
+
this.trackEvent({
|
|
5191
|
+
type: "queued",
|
|
5192
|
+
sourceId: request.sourceId,
|
|
5193
|
+
offset: request.offset,
|
|
5194
|
+
length: request.length,
|
|
5195
|
+
logicalRequestCount: 1,
|
|
5196
|
+
logicalBytes: request.length
|
|
5197
|
+
});
|
|
5198
|
+
this.scheduleFlush();
|
|
5199
|
+
return promise;
|
|
5200
|
+
}
|
|
5201
|
+
/**
|
|
5202
|
+
* Enqueues one HTTP byte-range request and resolves with the exact requested byte slice.
|
|
5203
|
+
*/
|
|
5204
|
+
fetch(request) {
|
|
5205
|
+
const fetchFunction = request.fetch || globalThis.fetch;
|
|
5206
|
+
return this.scheduleRequest({
|
|
5207
|
+
sourceId: request.sourceId || request.url,
|
|
5208
|
+
offset: request.offset,
|
|
5209
|
+
length: request.length,
|
|
5210
|
+
signal: request.signal,
|
|
5211
|
+
// Different fetch calls may carry different credentials or transport implementations.
|
|
5212
|
+
// Sharing is therefore opt-in through an explicit common isolation key.
|
|
5213
|
+
isolationKey: request.isolationKey ?? {},
|
|
5214
|
+
fetchRange: (offset, length4, signal) => fetchHttpRange({
|
|
5215
|
+
url: request.url,
|
|
5216
|
+
offset,
|
|
5217
|
+
length: length4,
|
|
5218
|
+
signal,
|
|
5219
|
+
fetch: fetchFunction,
|
|
5220
|
+
fetchOptions: request.fetchOptions
|
|
5221
|
+
})
|
|
5222
|
+
});
|
|
5223
|
+
}
|
|
5224
|
+
/** Immediately starts loading the currently queued requests. */
|
|
5225
|
+
flush() {
|
|
5226
|
+
if (this.flushTimer) {
|
|
5227
|
+
clearTimeout(this.flushTimer);
|
|
5228
|
+
this.flushTimer = null;
|
|
5229
|
+
}
|
|
5230
|
+
const pendingRequests = this.pendingRequests;
|
|
5231
|
+
this.pendingRequests = [];
|
|
5232
|
+
const activeRequests = pendingRequests.filter((request) => {
|
|
5233
|
+
if (request.settled || request.signal?.aborted) {
|
|
5234
|
+
this.abortRequest(request);
|
|
5235
|
+
return false;
|
|
5236
|
+
}
|
|
5237
|
+
return true;
|
|
5238
|
+
});
|
|
5239
|
+
const mergedRequests = this.mergeRequests(activeRequests);
|
|
5240
|
+
if (activeRequests.length > 0) {
|
|
5241
|
+
this.trackBatch(activeRequests, mergedRequests);
|
|
5242
|
+
}
|
|
5243
|
+
for (const mergedRequest of mergedRequests) {
|
|
5244
|
+
this.fetchMergedRequest(mergedRequest);
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
/** Schedules the next pending-request flush. */
|
|
5248
|
+
scheduleFlush() {
|
|
5249
|
+
if (this.flushTimer) {
|
|
5250
|
+
return;
|
|
5251
|
+
}
|
|
5252
|
+
this.flushTimer = setTimeout(() => this.flush(), this.props.batchDelayMs);
|
|
5253
|
+
}
|
|
5254
|
+
/** Sorts active requests and combines requests for the same source when thresholds allow. */
|
|
5255
|
+
mergeRequests(requests) {
|
|
5256
|
+
requests.sort((left, right) => {
|
|
5257
|
+
if (left.sourceId !== right.sourceId) {
|
|
5258
|
+
return left.sourceId < right.sourceId ? -1 : 1;
|
|
5259
|
+
}
|
|
5260
|
+
return left.offset - right.offset;
|
|
5261
|
+
});
|
|
5262
|
+
const mergedRequests = [];
|
|
5263
|
+
for (const request of requests) {
|
|
5264
|
+
const requestEndOffset = request.offset + request.length;
|
|
5265
|
+
const lastMergedRequest = mergedRequests[mergedRequests.length - 1];
|
|
5266
|
+
if (lastMergedRequest && canMergeRequests(lastMergedRequest, request, this.props)) {
|
|
5267
|
+
lastMergedRequest.endOffset = Math.max(lastMergedRequest.endOffset, requestEndOffset);
|
|
5268
|
+
lastMergedRequest.requests.push(request);
|
|
5269
|
+
} else {
|
|
5270
|
+
mergedRequests.push({
|
|
5271
|
+
sourceId: request.sourceId,
|
|
5272
|
+
isolationKey: request.isolationKey ?? request.sourceId,
|
|
5273
|
+
offset: request.offset,
|
|
5274
|
+
endOffset: requestEndOffset,
|
|
5275
|
+
fetchRange: request.fetchRange,
|
|
5276
|
+
requests: [request]
|
|
5277
|
+
});
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
return mergedRequests;
|
|
5281
|
+
}
|
|
5282
|
+
/** Loads one merged range and resolves each child request with its sliced bytes. */
|
|
5283
|
+
async fetchMergedRequest(mergedRequest) {
|
|
5284
|
+
const { offset, endOffset, fetchRange, requests } = mergedRequest;
|
|
5285
|
+
const abortController = new AbortController();
|
|
5286
|
+
const requestStartTime = getTimestamp();
|
|
5287
|
+
const abortTransportIfUnused = () => {
|
|
5288
|
+
if (requests.every((request) => request.signal?.aborted)) {
|
|
5289
|
+
abortController.abort();
|
|
5290
|
+
}
|
|
5291
|
+
};
|
|
5292
|
+
try {
|
|
5293
|
+
for (const request of requests) {
|
|
5294
|
+
request.onAbort = abortTransportIfUnused;
|
|
5295
|
+
}
|
|
5296
|
+
abortTransportIfUnused();
|
|
5297
|
+
if (requests.every((request) => request.settled)) {
|
|
5298
|
+
return;
|
|
5299
|
+
}
|
|
5300
|
+
const logicalBytes = getLogicalRequestBytes(requests);
|
|
5301
|
+
const length4 = endOffset - offset;
|
|
5302
|
+
this.trackEvent({
|
|
5303
|
+
type: "request",
|
|
5304
|
+
sourceId: mergedRequest.sourceId,
|
|
5305
|
+
offset,
|
|
5306
|
+
length: length4,
|
|
5307
|
+
logicalRequestCount: requests.length,
|
|
5308
|
+
logicalBytes,
|
|
5309
|
+
transportBytes: length4,
|
|
5310
|
+
overfetchBytes: Math.max(length4 - logicalBytes, 0)
|
|
5311
|
+
});
|
|
5312
|
+
const transportResult = normalizeTransportResult(
|
|
5313
|
+
await fetchRange(offset, length4, abortController.signal)
|
|
5314
|
+
);
|
|
5315
|
+
const { arrayBuffer } = transportResult;
|
|
5316
|
+
const isClampedFullResponse = transportResult.fullResponse && offset === 0 && arrayBuffer.byteLength < length4;
|
|
5317
|
+
const isClampedEndOfFile = arrayBuffer.byteLength < length4 && Number.isSafeInteger(transportResult.sourceByteLength) && offset + arrayBuffer.byteLength === transportResult.sourceByteLength;
|
|
5318
|
+
if (arrayBuffer.byteLength !== length4 && !isClampedFullResponse && !isClampedEndOfFile) {
|
|
5319
|
+
throw new Error(
|
|
5320
|
+
`Byte-range transport returned ${arrayBuffer.byteLength} bytes; expected ${length4}`
|
|
5321
|
+
);
|
|
5322
|
+
}
|
|
5323
|
+
const networkTimeMs = transportResult.networkTimeMs ?? getTimestamp() - requestStartTime;
|
|
5324
|
+
this.trackEvent({
|
|
5325
|
+
type: "response",
|
|
5326
|
+
sourceId: mergedRequest.sourceId,
|
|
5327
|
+
offset,
|
|
5328
|
+
length: length4,
|
|
5329
|
+
logicalRequestCount: requests.length,
|
|
5330
|
+
logicalBytes,
|
|
5331
|
+
transportBytes: length4,
|
|
5332
|
+
responseBytes: transportResult.transportBytes ?? arrayBuffer.byteLength,
|
|
5333
|
+
networkTimeMs,
|
|
5334
|
+
overfetchBytes: Math.max(length4 - logicalBytes, 0),
|
|
5335
|
+
status: transportResult.status,
|
|
5336
|
+
fullResponse: transportResult.fullResponse
|
|
5337
|
+
});
|
|
5338
|
+
for (const request of requests) {
|
|
5339
|
+
if (request.settled) {
|
|
5340
|
+
continue;
|
|
5341
|
+
}
|
|
5342
|
+
const start = request.offset - offset;
|
|
5343
|
+
request.settled = true;
|
|
5344
|
+
request.resolve(arrayBuffer.slice(start, start + request.length));
|
|
5345
|
+
}
|
|
5346
|
+
} catch (error) {
|
|
5347
|
+
this.trackEvent({
|
|
5348
|
+
type: "error",
|
|
5349
|
+
sourceId: mergedRequest.sourceId,
|
|
5350
|
+
offset,
|
|
5351
|
+
length: endOffset - offset,
|
|
5352
|
+
logicalRequestCount: requests.length,
|
|
5353
|
+
logicalBytes: getLogicalRequestBytes(requests),
|
|
5354
|
+
networkTimeMs: getTimestamp() - requestStartTime,
|
|
5355
|
+
error
|
|
5356
|
+
});
|
|
5357
|
+
for (const request of requests) {
|
|
5358
|
+
if (request.settled) {
|
|
5359
|
+
continue;
|
|
5360
|
+
}
|
|
5361
|
+
if (request.signal?.aborted) {
|
|
5362
|
+
this.abortRequest(request);
|
|
5363
|
+
} else {
|
|
5364
|
+
request.settled = true;
|
|
5365
|
+
request.reject(error);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
} finally {
|
|
5369
|
+
for (const request of requests) {
|
|
5370
|
+
if (request.abortListener) {
|
|
5371
|
+
request.signal?.removeEventListener("abort", request.abortListener);
|
|
5372
|
+
}
|
|
5373
|
+
request.onAbort = void 0;
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
/** Rejects and tracks one logical request exactly once when its caller aborts. */
|
|
5378
|
+
abortRequest(request) {
|
|
5379
|
+
if (request.settled) {
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
request.settled = true;
|
|
5383
|
+
request.reject(createAbortError());
|
|
5384
|
+
this.trackEvent({
|
|
5385
|
+
type: "abort",
|
|
5386
|
+
sourceId: request.sourceId,
|
|
5387
|
+
offset: request.offset,
|
|
5388
|
+
length: request.length,
|
|
5389
|
+
logicalRequestCount: 1,
|
|
5390
|
+
logicalBytes: request.length
|
|
5391
|
+
});
|
|
5392
|
+
}
|
|
5393
|
+
/** Emits one event to Stats and to the optional callback. */
|
|
5394
|
+
trackEvent(event) {
|
|
5395
|
+
trackStatsEvent(this.stats, event);
|
|
5396
|
+
try {
|
|
5397
|
+
this.onEvent?.(event);
|
|
5398
|
+
} catch {
|
|
5399
|
+
}
|
|
5400
|
+
}
|
|
5401
|
+
/** Emits one batch event after logical requests have been merged. */
|
|
5402
|
+
trackBatch(activeRequests, mergedRequests) {
|
|
5403
|
+
const logicalBytes = getLogicalRequestBytes(activeRequests);
|
|
5404
|
+
const transportBytes = mergedRequests.reduce(
|
|
5405
|
+
(sum, mergedRequest) => sum + mergedRequest.endOffset - mergedRequest.offset,
|
|
5406
|
+
0
|
|
5407
|
+
);
|
|
5408
|
+
this.trackEvent({
|
|
5409
|
+
type: "batch",
|
|
5410
|
+
logicalRequestCount: activeRequests.length,
|
|
5411
|
+
transportRequestCount: mergedRequests.length,
|
|
5412
|
+
logicalBytes,
|
|
5413
|
+
transportBytes,
|
|
5414
|
+
overfetchBytes: Math.max(transportBytes - logicalBytes, 0)
|
|
5415
|
+
});
|
|
5416
|
+
}
|
|
5417
|
+
};
|
|
5418
|
+
function trackStatsEvent(stats, event) {
|
|
5419
|
+
if ((event.type === "response" || event.type === "error") && event.networkTimeMs !== void 0) {
|
|
5420
|
+
stats.get(RANGE_STATS_KEYS.networkTimeMs, "time").addTime(event.networkTimeMs);
|
|
5421
|
+
}
|
|
5422
|
+
switch (event.type) {
|
|
5423
|
+
case "queued":
|
|
5424
|
+
stats.get(RANGE_STATS_KEYS.logicalRanges, "count").incrementCount();
|
|
5425
|
+
stats.get(RANGE_STATS_KEYS.requestedBytes, "count").addCount(event.logicalBytes || 0);
|
|
5426
|
+
break;
|
|
5427
|
+
case "batch":
|
|
5428
|
+
stats.get(RANGE_STATS_KEYS.rangeBatches, "count").incrementCount();
|
|
5429
|
+
stats.get("Logical Ranges Batched", "count").addCount(event.logicalRequestCount || 0);
|
|
5430
|
+
stats.get(RANGE_STATS_KEYS.transportRanges, "count").addCount(event.transportRequestCount || 0);
|
|
5431
|
+
stats.get(RANGE_STATS_KEYS.coalescedRanges, "count").addCount(
|
|
5432
|
+
Math.max((event.logicalRequestCount || 0) - (event.transportRequestCount || 0), 0)
|
|
5433
|
+
);
|
|
5434
|
+
stats.get(RANGE_STATS_KEYS.transportBytes, "count").addCount(event.transportBytes || 0);
|
|
5435
|
+
stats.get(RANGE_STATS_KEYS.overfetchBytes, "count").addCount(event.overfetchBytes || 0);
|
|
5436
|
+
break;
|
|
5437
|
+
case "response":
|
|
5438
|
+
stats.get(RANGE_STATS_KEYS.completedTransportRanges, "count").incrementCount();
|
|
5439
|
+
stats.get(RANGE_STATS_KEYS.responseBytes, "count").addCount(event.responseBytes || 0);
|
|
5440
|
+
if (event.fullResponse) {
|
|
5441
|
+
stats.get(RANGE_STATS_KEYS.fullResponseFallbacks, "count").incrementCount();
|
|
5442
|
+
}
|
|
5443
|
+
break;
|
|
5444
|
+
case "error":
|
|
5445
|
+
stats.get(RANGE_STATS_KEYS.failedTransportRanges, "count").incrementCount();
|
|
5446
|
+
break;
|
|
5447
|
+
case "abort":
|
|
5448
|
+
stats.get(RANGE_STATS_KEYS.abortedLogicalRanges, "count").incrementCount();
|
|
5449
|
+
break;
|
|
5450
|
+
default:
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
function createRangeStats(id = "range-request-scheduler") {
|
|
5454
|
+
const stats = new Stats({ id });
|
|
5455
|
+
initializeStats(stats);
|
|
5456
|
+
return stats;
|
|
5457
|
+
}
|
|
5458
|
+
async function fetchHttpRange(request) {
|
|
5459
|
+
const abortContext = createAbortableFetchContext(request.signal);
|
|
5460
|
+
const requestStartTime = getTimestamp();
|
|
5461
|
+
try {
|
|
5462
|
+
let fullResponse = false;
|
|
5463
|
+
let response = await request.fetch(
|
|
5464
|
+
request.url,
|
|
5465
|
+
createRangeFetchOptions(
|
|
5466
|
+
request.fetchOptions,
|
|
5467
|
+
request.offset,
|
|
5468
|
+
request.length,
|
|
5469
|
+
abortContext.signal
|
|
5470
|
+
)
|
|
5471
|
+
);
|
|
5472
|
+
if (response.status === 416 && request.offset === 0) {
|
|
5473
|
+
const actualLength = parseUnsatisfiedContentRange(response.headers.get("Content-Range"));
|
|
5474
|
+
if (!actualLength) {
|
|
5475
|
+
throw new Error("Missing content-length on 416 response");
|
|
5476
|
+
}
|
|
5477
|
+
response = await request.fetch(
|
|
5478
|
+
request.url,
|
|
5479
|
+
createRangeFetchOptions(request.fetchOptions, 0, actualLength, abortContext.signal)
|
|
5480
|
+
);
|
|
5481
|
+
fullResponse = true;
|
|
5482
|
+
}
|
|
5483
|
+
if (response.status === 200) {
|
|
5484
|
+
abortContext.abort();
|
|
5485
|
+
await cancelIgnoredRangeResponse(response);
|
|
5486
|
+
throw new Error("Byte-range request failed: server returned 200 instead of 206");
|
|
5487
|
+
}
|
|
5488
|
+
if (response.status !== 206 && !response.ok) {
|
|
5489
|
+
throw new Error(`Bad response code: ${response.status}`);
|
|
5490
|
+
}
|
|
5491
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
5492
|
+
const contentRangeHeader = response.headers.get("Content-Range");
|
|
5493
|
+
const contentRange = parseSatisfiedContentRange(contentRangeHeader);
|
|
5494
|
+
if (contentRangeHeader && !contentRange) {
|
|
5495
|
+
throw new Error(`Invalid Content-Range header: ${contentRangeHeader}`);
|
|
5496
|
+
}
|
|
5497
|
+
if (contentRange) {
|
|
5498
|
+
const responseLength = contentRange.endOffset - contentRange.offset + 1;
|
|
5499
|
+
const requestedEndOffset = request.offset + request.length - 1;
|
|
5500
|
+
if (contentRange.offset !== request.offset || contentRange.endOffset > requestedEndOffset || responseLength !== arrayBuffer.byteLength || contentRange.sourceByteLength !== void 0 && contentRange.endOffset >= contentRange.sourceByteLength) {
|
|
5501
|
+
throw new Error(`Content-Range does not match requested range: ${contentRangeHeader}`);
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5504
|
+
return {
|
|
5505
|
+
arrayBuffer,
|
|
5506
|
+
status: response.status,
|
|
5507
|
+
sourceByteLength: contentRange?.sourceByteLength,
|
|
5508
|
+
transportBytes: arrayBuffer.byteLength,
|
|
5509
|
+
networkTimeMs: getTimestamp() - requestStartTime,
|
|
5510
|
+
fullResponse
|
|
5511
|
+
};
|
|
5512
|
+
} finally {
|
|
5513
|
+
abortContext.removeAbortListener();
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
function canMergeRequests(mergedRequest, request, props) {
|
|
5517
|
+
if (mergedRequest.sourceId !== request.sourceId) {
|
|
5518
|
+
return false;
|
|
5519
|
+
}
|
|
5520
|
+
if (mergedRequest.isolationKey !== (request.isolationKey ?? request.sourceId)) {
|
|
5521
|
+
return false;
|
|
5522
|
+
}
|
|
5523
|
+
const requestEndOffset = request.offset + request.length;
|
|
5524
|
+
const gapBytes = request.offset - mergedRequest.endOffset;
|
|
5525
|
+
const mergedBytes = Math.max(mergedRequest.endOffset, requestEndOffset) - mergedRequest.offset;
|
|
5526
|
+
return gapBytes <= props.rangeExpansionBytes && mergedBytes <= props.maxMergedBytes;
|
|
5527
|
+
}
|
|
5528
|
+
function initializeStats(stats) {
|
|
5529
|
+
stats.get(RANGE_STATS_KEYS.logicalRanges, "count");
|
|
5530
|
+
stats.get(RANGE_STATS_KEYS.requestedBytes, "count");
|
|
5531
|
+
stats.get(RANGE_STATS_KEYS.rangeBatches, "count");
|
|
5532
|
+
stats.get("Logical Ranges Batched", "count");
|
|
5533
|
+
stats.get(RANGE_STATS_KEYS.transportRanges, "count");
|
|
5534
|
+
stats.get(RANGE_STATS_KEYS.coalescedRanges, "count");
|
|
5535
|
+
stats.get(RANGE_STATS_KEYS.transportBytes, "count");
|
|
5536
|
+
stats.get(RANGE_STATS_KEYS.completedTransportRanges, "count");
|
|
5537
|
+
stats.get(RANGE_STATS_KEYS.responseBytes, "count");
|
|
5538
|
+
stats.get(RANGE_STATS_KEYS.networkTimeMs, "time");
|
|
5539
|
+
stats.get(RANGE_STATS_KEYS.overfetchBytes, "count");
|
|
5540
|
+
stats.get(RANGE_STATS_KEYS.fullResponseFallbacks, "count");
|
|
5541
|
+
stats.get(RANGE_STATS_KEYS.failedTransportRanges, "count");
|
|
5542
|
+
stats.get(RANGE_STATS_KEYS.abortedLogicalRanges, "count");
|
|
5543
|
+
}
|
|
5544
|
+
function getLogicalRequestBytes(requests) {
|
|
5545
|
+
return requests.reduce((sum, request) => sum + request.length, 0);
|
|
5546
|
+
}
|
|
5547
|
+
function normalizeTransportResult(result) {
|
|
5548
|
+
return result instanceof ArrayBuffer ? { arrayBuffer: result } : result;
|
|
5549
|
+
}
|
|
5550
|
+
function createRangeFetchOptions(fetchOptions, offset, length4, signal) {
|
|
5551
|
+
return {
|
|
5552
|
+
...fetchOptions,
|
|
5553
|
+
signal,
|
|
5554
|
+
headers: {
|
|
5555
|
+
...getHeadersObject(fetchOptions?.headers),
|
|
5556
|
+
Range: `bytes=${offset}-${offset + length4 - 1}`
|
|
5557
|
+
}
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
function getHeadersObject(headers) {
|
|
5561
|
+
if (!headers) {
|
|
5562
|
+
return {};
|
|
5563
|
+
}
|
|
5564
|
+
if (headers instanceof Headers) {
|
|
5565
|
+
return Object.fromEntries(headers.entries());
|
|
5566
|
+
}
|
|
5567
|
+
if (Array.isArray(headers)) {
|
|
5568
|
+
return Object.fromEntries(headers);
|
|
5569
|
+
}
|
|
5570
|
+
return headers;
|
|
5571
|
+
}
|
|
5572
|
+
function parseUnsatisfiedContentRange(contentRange) {
|
|
5573
|
+
const match = contentRange?.match(/^bytes \*\/(\d+)$/);
|
|
5574
|
+
return match ? Number(match[1]) : null;
|
|
5575
|
+
}
|
|
5576
|
+
function parseSatisfiedContentRange(contentRange) {
|
|
5577
|
+
const match = contentRange?.match(/^bytes (\d+)-(\d+)\/(\d+|\*)$/);
|
|
5578
|
+
if (!match) {
|
|
5579
|
+
return null;
|
|
5580
|
+
}
|
|
5581
|
+
const offset = Number(match[1]);
|
|
5582
|
+
const endOffset = Number(match[2]);
|
|
5583
|
+
const sourceByteLength = match[3] === "*" ? void 0 : Number(match[3]);
|
|
5584
|
+
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(endOffset) || offset > endOffset || sourceByteLength !== void 0 && !Number.isSafeInteger(sourceByteLength)) {
|
|
5585
|
+
return null;
|
|
5586
|
+
}
|
|
5587
|
+
return { offset, endOffset, sourceByteLength };
|
|
5588
|
+
}
|
|
5589
|
+
function createAbortableFetchContext(parentSignal) {
|
|
5590
|
+
const abortController = new AbortController();
|
|
5591
|
+
const abortListener = () => abortController.abort();
|
|
5592
|
+
if (parentSignal?.aborted) {
|
|
5593
|
+
abortController.abort();
|
|
5594
|
+
} else {
|
|
5595
|
+
parentSignal?.addEventListener("abort", abortListener, { once: true });
|
|
5596
|
+
}
|
|
5597
|
+
return {
|
|
5598
|
+
signal: abortController.signal,
|
|
5599
|
+
abort: () => abortController.abort(),
|
|
5600
|
+
removeAbortListener: () => parentSignal?.removeEventListener("abort", abortListener)
|
|
5601
|
+
};
|
|
5602
|
+
}
|
|
5603
|
+
async function cancelIgnoredRangeResponse(response) {
|
|
5604
|
+
await response.body?.cancel().catch(() => {
|
|
5605
|
+
});
|
|
5606
|
+
}
|
|
5607
|
+
function getTimestamp() {
|
|
5608
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
5609
|
+
}
|
|
5610
|
+
function createAbortError() {
|
|
5611
|
+
if (typeof DOMException !== "undefined") {
|
|
5612
|
+
return new DOMException("Request aborted", "AbortError");
|
|
5613
|
+
}
|
|
5614
|
+
const error = new Error("Request aborted");
|
|
5615
|
+
error.name = "AbortError";
|
|
5616
|
+
return error;
|
|
5617
|
+
}
|
|
5618
|
+
|
|
5109
5619
|
// ../loader-utils/src/lib/path-utils/path.ts
|
|
5110
5620
|
var path_exports = {};
|
|
5111
5621
|
__export(path_exports, {
|
|
@@ -5270,97 +5780,513 @@ var __exports__ = (() => {
|
|
|
5270
5780
|
isDirectory: false
|
|
5271
5781
|
};
|
|
5272
5782
|
}
|
|
5273
|
-
async read(start, length4) {
|
|
5783
|
+
async read(start, length4, signal) {
|
|
5784
|
+
if (signal?.aborted) {
|
|
5785
|
+
throw new Error("Request aborted");
|
|
5786
|
+
}
|
|
5274
5787
|
const arrayBuffer = await this.handle.slice(Number(start), Number(start) + Number(length4)).arrayBuffer();
|
|
5788
|
+
if (signal?.aborted) {
|
|
5789
|
+
throw new Error("Request aborted");
|
|
5790
|
+
}
|
|
5275
5791
|
return arrayBuffer;
|
|
5276
5792
|
}
|
|
5277
5793
|
};
|
|
5278
5794
|
|
|
5795
|
+
// ../loader-utils/src/lib/files/http-file-transport.ts
|
|
5796
|
+
var HttpFileTransport = class {
|
|
5797
|
+
/** Remote object URL. */
|
|
5798
|
+
url;
|
|
5799
|
+
/** Fetch implementation used for range requests. */
|
|
5800
|
+
fetchFunction;
|
|
5801
|
+
/** Persistent fetch options copied from the caller. */
|
|
5802
|
+
fetchOptions;
|
|
5803
|
+
/** Validator enforcement mode. */
|
|
5804
|
+
consistency;
|
|
5805
|
+
/** Caller-supplied identity information. */
|
|
5806
|
+
identityHints;
|
|
5807
|
+
/** Failure objects already included in telemetry. */
|
|
5808
|
+
countedFailures = /* @__PURE__ */ new WeakSet();
|
|
5809
|
+
/** Mutable counters backing immutable telemetry snapshots. */
|
|
5810
|
+
telemetry = {
|
|
5811
|
+
requestedBytes: 0,
|
|
5812
|
+
downloadedBytes: 0,
|
|
5813
|
+
requestCount: 0,
|
|
5814
|
+
networkTimeMs: 0,
|
|
5815
|
+
abortCount: 0,
|
|
5816
|
+
errorCount: 0
|
|
5817
|
+
};
|
|
5818
|
+
/** Creates the transport for one remote object. */
|
|
5819
|
+
constructor(url, options) {
|
|
5820
|
+
validateKnownByteLength(options.byteLength);
|
|
5821
|
+
this.url = url;
|
|
5822
|
+
this.fetchFunction = options.fetch || ((input, init) => globalThis.fetch(input, init));
|
|
5823
|
+
this.fetchOptions = options.fetchOptions ? { ...options.fetchOptions, headers: new Headers(options.fetchOptions.headers) } : void 0;
|
|
5824
|
+
this.consistency = options.consistency || "best-effort";
|
|
5825
|
+
this.identityHints = {
|
|
5826
|
+
byteLength: options.byteLength,
|
|
5827
|
+
etag: normalizeHeaderValue(options.etag),
|
|
5828
|
+
lastModified: normalizeHeaderValue(options.lastModified)
|
|
5829
|
+
};
|
|
5830
|
+
}
|
|
5831
|
+
/** Returns an identity that is complete enough to avoid an opening request. */
|
|
5832
|
+
getInitialIdentity() {
|
|
5833
|
+
const { byteLength, etag, lastModified } = this.identityHints;
|
|
5834
|
+
if (byteLength !== void 0 && (etag !== void 0 || lastModified !== void 0)) {
|
|
5835
|
+
return createIdentity(byteLength, etag, lastModified);
|
|
5836
|
+
}
|
|
5837
|
+
if (byteLength === 0 && this.consistency === "best-effort") {
|
|
5838
|
+
return createIdentity(0);
|
|
5839
|
+
}
|
|
5840
|
+
return null;
|
|
5841
|
+
}
|
|
5842
|
+
/** Returns an immutable point-in-time copy of the transport counters. */
|
|
5843
|
+
getTelemetry() {
|
|
5844
|
+
return Object.freeze({ ...this.telemetry });
|
|
5845
|
+
}
|
|
5846
|
+
/** Counts each failure object once even when it passes through several async layers. */
|
|
5847
|
+
trackFailure(error) {
|
|
5848
|
+
if (typeof error === "object" && error !== null) {
|
|
5849
|
+
if (this.countedFailures.has(error)) {
|
|
5850
|
+
return;
|
|
5851
|
+
}
|
|
5852
|
+
this.countedFailures.add(error);
|
|
5853
|
+
}
|
|
5854
|
+
if (isAbortError(error)) {
|
|
5855
|
+
this.telemetry.abortCount++;
|
|
5856
|
+
} else {
|
|
5857
|
+
this.telemetry.errorCount++;
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
/** Performs and validates one exact HTTP range request. */
|
|
5861
|
+
async requestRange(offset, length4, signal, expectedIdentity) {
|
|
5862
|
+
const requestStartTime = getTimestamp2();
|
|
5863
|
+
const abortContext = createCombinedAbortContext(signal, this.fetchOptions?.signal);
|
|
5864
|
+
this.telemetry.requestCount++;
|
|
5865
|
+
this.telemetry.requestedBytes += length4;
|
|
5866
|
+
try {
|
|
5867
|
+
const response = await this.fetchFunction(
|
|
5868
|
+
this.url,
|
|
5869
|
+
createFetchOptions(this.fetchOptions, offset, length4, abortContext.signal)
|
|
5870
|
+
);
|
|
5871
|
+
if (isEmptyIdentityProbeResponse(response, offset, length4, expectedIdentity)) {
|
|
5872
|
+
return await this.readEmptyIdentityProbe(response, requestStartTime);
|
|
5873
|
+
}
|
|
5874
|
+
if (response.status !== 206) {
|
|
5875
|
+
await cancelResponse(response);
|
|
5876
|
+
throw new Error(`HTTP byte-range request expected 206, received ${response.status}`);
|
|
5877
|
+
}
|
|
5878
|
+
const contentRange = await readContentRange(response);
|
|
5879
|
+
const responseIdentity = getResponseIdentity(response, contentRange.byteLength);
|
|
5880
|
+
const responseError = getIdentityError(
|
|
5881
|
+
expectedIdentity,
|
|
5882
|
+
responseIdentity,
|
|
5883
|
+
this.identityHints,
|
|
5884
|
+
this.consistency
|
|
5885
|
+
) || getRangeError(contentRange, offset, length4);
|
|
5886
|
+
if (responseError) {
|
|
5887
|
+
await cancelResponse(response);
|
|
5888
|
+
throw new Error(responseError);
|
|
5889
|
+
}
|
|
5890
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
5891
|
+
this.telemetry.downloadedBytes += arrayBuffer.byteLength;
|
|
5892
|
+
if (arrayBuffer.byteLength !== length4) {
|
|
5893
|
+
throw new Error(
|
|
5894
|
+
`HTTP byte-range response contained ${arrayBuffer.byteLength} bytes; expected ${length4}`
|
|
5895
|
+
);
|
|
5896
|
+
}
|
|
5897
|
+
return {
|
|
5898
|
+
arrayBuffer,
|
|
5899
|
+
identity: resolveIdentity(expectedIdentity, responseIdentity, this.identityHints),
|
|
5900
|
+
status: response.status,
|
|
5901
|
+
transportBytes: arrayBuffer.byteLength,
|
|
5902
|
+
networkTimeMs: getTimestamp2() - requestStartTime
|
|
5903
|
+
};
|
|
5904
|
+
} catch (error) {
|
|
5905
|
+
if (expectedIdentity === null || !isAbortError(error)) {
|
|
5906
|
+
this.trackFailure(error);
|
|
5907
|
+
}
|
|
5908
|
+
throw error;
|
|
5909
|
+
} finally {
|
|
5910
|
+
abortContext.removeAbortListeners();
|
|
5911
|
+
this.telemetry.networkTimeMs += getTimestamp2() - requestStartTime;
|
|
5912
|
+
}
|
|
5913
|
+
}
|
|
5914
|
+
/** Validates and consumes the special empty-object identity response. */
|
|
5915
|
+
async readEmptyIdentityProbe(response, requestStartTime) {
|
|
5916
|
+
const responseIdentity = getResponseIdentity(response, 0);
|
|
5917
|
+
const identityError = getIdentityError(
|
|
5918
|
+
null,
|
|
5919
|
+
responseIdentity,
|
|
5920
|
+
this.identityHints,
|
|
5921
|
+
this.consistency
|
|
5922
|
+
);
|
|
5923
|
+
await cancelResponse(response);
|
|
5924
|
+
if (identityError) {
|
|
5925
|
+
throw new Error(identityError);
|
|
5926
|
+
}
|
|
5927
|
+
return {
|
|
5928
|
+
arrayBuffer: new ArrayBuffer(0),
|
|
5929
|
+
identity: resolveIdentity(null, responseIdentity, this.identityHints),
|
|
5930
|
+
status: response.status,
|
|
5931
|
+
transportBytes: 0,
|
|
5932
|
+
networkTimeMs: getTimestamp2() - requestStartTime
|
|
5933
|
+
};
|
|
5934
|
+
}
|
|
5935
|
+
};
|
|
5936
|
+
function createFetchOptions(fetchOptions, offset, length4, signal) {
|
|
5937
|
+
const headers = new Headers(fetchOptions?.headers);
|
|
5938
|
+
headers.set("Range", `bytes=${offset}-${offset + length4 - 1}`);
|
|
5939
|
+
return {
|
|
5940
|
+
...fetchOptions,
|
|
5941
|
+
method: "GET",
|
|
5942
|
+
body: void 0,
|
|
5943
|
+
headers,
|
|
5944
|
+
signal
|
|
5945
|
+
};
|
|
5946
|
+
}
|
|
5947
|
+
function isEmptyIdentityProbeResponse(response, offset, length4, expectedIdentity) {
|
|
5948
|
+
return response.status === 416 && expectedIdentity === null && offset === 0 && length4 === 1 && /^bytes \*\/0$/i.test(response.headers.get("Content-Range") || "");
|
|
5949
|
+
}
|
|
5950
|
+
async function readContentRange(response) {
|
|
5951
|
+
try {
|
|
5952
|
+
return parseContentRange(response.headers.get("Content-Range"));
|
|
5953
|
+
} catch (error) {
|
|
5954
|
+
await cancelResponse(response);
|
|
5955
|
+
throw error;
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5958
|
+
function parseContentRange(contentRange) {
|
|
5959
|
+
const match = contentRange?.match(/^bytes (\d+)-(\d+)\/(\d+)$/);
|
|
5960
|
+
if (!match) {
|
|
5961
|
+
throw new Error("HTTP byte-range response has an invalid Content-Range header");
|
|
5962
|
+
}
|
|
5963
|
+
return {
|
|
5964
|
+
offset: Number(match[1]),
|
|
5965
|
+
endOffset: Number(match[2]),
|
|
5966
|
+
byteLength: Number(match[3])
|
|
5967
|
+
};
|
|
5968
|
+
}
|
|
5969
|
+
function getResponseIdentity(response, byteLength) {
|
|
5970
|
+
return createIdentity(
|
|
5971
|
+
byteLength,
|
|
5972
|
+
normalizeHeaderValue(response.headers.get("ETag")),
|
|
5973
|
+
normalizeHeaderValue(response.headers.get("Last-Modified"))
|
|
5974
|
+
);
|
|
5975
|
+
}
|
|
5976
|
+
function resolveIdentity(expectedIdentity, responseIdentity, identityHints) {
|
|
5977
|
+
return expectedIdentity || createIdentity(
|
|
5978
|
+
responseIdentity.byteLength,
|
|
5979
|
+
responseIdentity.etag ?? identityHints.etag,
|
|
5980
|
+
responseIdentity.lastModified ?? identityHints.lastModified
|
|
5981
|
+
);
|
|
5982
|
+
}
|
|
5983
|
+
function getRangeError(contentRange, offset, length4) {
|
|
5984
|
+
if (!Number.isSafeInteger(contentRange.offset) || !Number.isSafeInteger(contentRange.endOffset) || !Number.isSafeInteger(contentRange.byteLength) || contentRange.offset < 0 || contentRange.endOffset < contentRange.offset || contentRange.endOffset >= contentRange.byteLength) {
|
|
5985
|
+
return "HTTP byte-range response has invalid range bounds";
|
|
5986
|
+
}
|
|
5987
|
+
if (contentRange.offset !== offset || contentRange.endOffset !== offset + length4 - 1) {
|
|
5988
|
+
return "HTTP byte-range response does not match the requested range";
|
|
5989
|
+
}
|
|
5990
|
+
return null;
|
|
5991
|
+
}
|
|
5992
|
+
function getIdentityError(expectedIdentity, responseIdentity, identityHints, consistency) {
|
|
5993
|
+
const expectedByteLength = expectedIdentity?.byteLength ?? identityHints.byteLength;
|
|
5994
|
+
if (expectedByteLength !== void 0 && responseIdentity.byteLength !== expectedByteLength) {
|
|
5995
|
+
return "HTTP file length changed while the file was open";
|
|
5996
|
+
}
|
|
5997
|
+
const expectedEtag = expectedIdentity?.etag ?? identityHints.etag;
|
|
5998
|
+
const expectedLastModified = expectedIdentity?.lastModified ?? identityHints.lastModified;
|
|
5999
|
+
if (expectedEtag) {
|
|
6000
|
+
if (responseIdentity.etag) {
|
|
6001
|
+
return expectedEtag === responseIdentity.etag ? null : "HTTP file ETag changed while the file was open";
|
|
6002
|
+
}
|
|
6003
|
+
if (consistency === "strict") {
|
|
6004
|
+
return "HTTP byte-range response is missing the pinned ETag";
|
|
6005
|
+
}
|
|
6006
|
+
if (expectedLastModified && responseIdentity.lastModified && expectedLastModified !== responseIdentity.lastModified) {
|
|
6007
|
+
return "HTTP file Last-Modified value changed while the file was open";
|
|
6008
|
+
}
|
|
6009
|
+
return null;
|
|
6010
|
+
}
|
|
6011
|
+
if (expectedLastModified) {
|
|
6012
|
+
if (responseIdentity.lastModified && expectedLastModified !== responseIdentity.lastModified) {
|
|
6013
|
+
return "HTTP file Last-Modified value changed while the file was open";
|
|
6014
|
+
}
|
|
6015
|
+
if (consistency === "strict" && !responseIdentity.lastModified) {
|
|
6016
|
+
return "HTTP byte-range response is missing the pinned Last-Modified value";
|
|
6017
|
+
}
|
|
6018
|
+
return null;
|
|
6019
|
+
}
|
|
6020
|
+
if (consistency === "strict" && !responseIdentity.etag && !responseIdentity.lastModified) {
|
|
6021
|
+
return "HTTP byte-range response does not provide an ETag or Last-Modified validator";
|
|
6022
|
+
}
|
|
6023
|
+
return null;
|
|
6024
|
+
}
|
|
6025
|
+
function createIdentity(byteLength, etag, lastModified) {
|
|
6026
|
+
return Object.freeze({ byteLength, etag, lastModified });
|
|
6027
|
+
}
|
|
6028
|
+
function validateKnownByteLength(byteLength) {
|
|
6029
|
+
if (byteLength !== void 0 && (!Number.isSafeInteger(byteLength) || byteLength < 0)) {
|
|
6030
|
+
throw new Error("HttpFile byteLength must be a non-negative safe integer");
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
async function cancelResponse(response) {
|
|
6034
|
+
await response.body?.cancel().catch(() => {
|
|
6035
|
+
});
|
|
6036
|
+
}
|
|
6037
|
+
function normalizeHeaderValue(value) {
|
|
6038
|
+
return value || void 0;
|
|
6039
|
+
}
|
|
6040
|
+
function getTimestamp2() {
|
|
6041
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
6042
|
+
}
|
|
6043
|
+
function isAbortError(error) {
|
|
6044
|
+
return error instanceof Error && error.name === "AbortError" || error instanceof Error && /aborted/i.test(error.message);
|
|
6045
|
+
}
|
|
6046
|
+
function createCombinedAbortContext(...signals) {
|
|
6047
|
+
const abortController = new AbortController();
|
|
6048
|
+
const uniqueSignals = [
|
|
6049
|
+
...new Set(signals.filter((signal) => Boolean(signal)))
|
|
6050
|
+
];
|
|
6051
|
+
const abortListener = () => abortController.abort();
|
|
6052
|
+
for (const signal of uniqueSignals) {
|
|
6053
|
+
if (signal.aborted) {
|
|
6054
|
+
abortController.abort();
|
|
6055
|
+
} else {
|
|
6056
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
return {
|
|
6060
|
+
signal: abortController.signal,
|
|
6061
|
+
removeAbortListeners: () => {
|
|
6062
|
+
for (const signal of uniqueSignals) {
|
|
6063
|
+
signal.removeEventListener("abort", abortListener);
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
6066
|
+
};
|
|
6067
|
+
}
|
|
6068
|
+
|
|
5279
6069
|
// ../loader-utils/src/lib/files/http-file.ts
|
|
5280
|
-
var HttpFile = class {
|
|
6070
|
+
var HttpFile = class _HttpFile {
|
|
6071
|
+
/** URL used as the ReadableFile handle. */
|
|
5281
6072
|
handle;
|
|
5282
|
-
|
|
5283
|
-
bigsize = 0n;
|
|
6073
|
+
/** Remote object URL. */
|
|
5284
6074
|
url;
|
|
5285
|
-
|
|
6075
|
+
/** HTTP transport and response validator for this file. */
|
|
6076
|
+
transport;
|
|
6077
|
+
/** Scheduler used to coalesce compatible range reads. */
|
|
6078
|
+
rangeScheduler;
|
|
6079
|
+
/** Unique key that prevents coalescing across transport or credential contexts. */
|
|
6080
|
+
schedulerIsolationKey = {};
|
|
6081
|
+
/** Caller-supplied length available before identity discovery. */
|
|
6082
|
+
suppliedByteLength;
|
|
6083
|
+
/** Pinned remote object identity. */
|
|
6084
|
+
identity;
|
|
6085
|
+
/** Shared opening probe, while identity discovery is in progress. */
|
|
6086
|
+
openPromise = null;
|
|
6087
|
+
/** Whether the file has stopped accepting operations. */
|
|
6088
|
+
closed = false;
|
|
6089
|
+
/** Creates a lazy HTTP file. Use {@link HttpFile.open} when identity must be pinned immediately. */
|
|
6090
|
+
constructor(url, options = {}) {
|
|
5286
6091
|
this.handle = url;
|
|
5287
6092
|
this.url = url;
|
|
6093
|
+
this.transport = new HttpFileTransport(url, options);
|
|
6094
|
+
this.rangeScheduler = options.rangeScheduler || new RangeRequestScheduler({ batchDelayMs: 0, ...options.rangeSchedulerProps });
|
|
6095
|
+
this.suppliedByteLength = options.byteLength;
|
|
6096
|
+
this.identity = this.transport.getInitialIdentity();
|
|
6097
|
+
}
|
|
6098
|
+
/** Opens a file and pins its byte length and available validators. */
|
|
6099
|
+
static async open(url, options = {}, signal) {
|
|
6100
|
+
const file = new _HttpFile(url, options);
|
|
6101
|
+
await file.open(signal);
|
|
6102
|
+
return file;
|
|
6103
|
+
}
|
|
6104
|
+
/** Pins this file's byte length and available validators. */
|
|
6105
|
+
async open(signal) {
|
|
6106
|
+
this.assertOpen();
|
|
6107
|
+
if (signal?.aborted) {
|
|
6108
|
+
const error = createAbortError2();
|
|
6109
|
+
this.transport.trackFailure(error);
|
|
6110
|
+
throw error;
|
|
6111
|
+
}
|
|
6112
|
+
await this.getIdentity(signal);
|
|
6113
|
+
return this;
|
|
5288
6114
|
}
|
|
6115
|
+
/** Pinned file length, or zero before a lazy file has opened. */
|
|
6116
|
+
get size() {
|
|
6117
|
+
return this.identity?.byteLength ?? this.suppliedByteLength ?? 0;
|
|
6118
|
+
}
|
|
6119
|
+
/** Pinned file length as a bigint, or zero before a lazy file has opened. */
|
|
6120
|
+
get bigsize() {
|
|
6121
|
+
return BigInt(this.size);
|
|
6122
|
+
}
|
|
6123
|
+
/** Returns the pinned identity, or null before a lazy file has opened. */
|
|
6124
|
+
getIdentitySnapshot() {
|
|
6125
|
+
return this.identity;
|
|
6126
|
+
}
|
|
6127
|
+
/** Returns an immutable point-in-time copy of this file's transport counters. */
|
|
6128
|
+
getTelemetry() {
|
|
6129
|
+
return this.transport.getTelemetry();
|
|
6130
|
+
}
|
|
6131
|
+
/** Prevents new operations. Active fetches remain controlled by their read signals. */
|
|
5289
6132
|
async close() {
|
|
6133
|
+
this.closed = true;
|
|
5290
6134
|
}
|
|
6135
|
+
/** Returns pinned file information without issuing a HEAD request. */
|
|
5291
6136
|
async stat() {
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
throw new Error(`Failed to fetch HEAD ${this.handle}`);
|
|
5295
|
-
}
|
|
5296
|
-
const size = parseInt(response.headers.get("Content-Length") || "0");
|
|
6137
|
+
this.assertOpen();
|
|
6138
|
+
const identity4 = await this.getIdentity();
|
|
5297
6139
|
return {
|
|
5298
|
-
size,
|
|
5299
|
-
bigsize: BigInt(
|
|
6140
|
+
size: identity4.byteLength,
|
|
6141
|
+
bigsize: BigInt(identity4.byteLength),
|
|
5300
6142
|
isDirectory: false
|
|
5301
6143
|
};
|
|
5302
6144
|
}
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
6145
|
+
/** Reads exactly one byte range, with optional cancellation. */
|
|
6146
|
+
async read(offset = 0, length4 = 0, signal) {
|
|
6147
|
+
this.assertOpen();
|
|
6148
|
+
if (signal?.aborted) {
|
|
6149
|
+
const error = createAbortError2();
|
|
6150
|
+
this.transport.trackFailure(error);
|
|
6151
|
+
throw error;
|
|
6152
|
+
}
|
|
6153
|
+
const identity4 = await this.getIdentity(signal);
|
|
6154
|
+
const numericOffset = normalizeOffset(offset);
|
|
6155
|
+
validateRange(numericOffset, length4, identity4.byteLength);
|
|
6156
|
+
if (length4 === 0) {
|
|
6157
|
+
return new ArrayBuffer(0);
|
|
6158
|
+
}
|
|
6159
|
+
try {
|
|
6160
|
+
return await this.rangeScheduler.scheduleRequest({
|
|
6161
|
+
sourceId: this.url,
|
|
6162
|
+
isolationKey: this.schedulerIsolationKey,
|
|
6163
|
+
offset: numericOffset,
|
|
6164
|
+
length: length4,
|
|
6165
|
+
signal,
|
|
6166
|
+
fetchRange: this.fetchScheduledRange
|
|
6167
|
+
});
|
|
6168
|
+
} catch (error) {
|
|
6169
|
+
this.transport.trackFailure(error);
|
|
6170
|
+
throw error;
|
|
6171
|
+
}
|
|
5307
6172
|
}
|
|
5308
|
-
/**
|
|
5309
|
-
*
|
|
5310
|
-
* @param offset
|
|
5311
|
-
* @param length
|
|
5312
|
-
* @param signal
|
|
5313
|
-
* @returns
|
|
5314
|
-
* @see https://github.com/protomaps/PMTiles
|
|
5315
|
-
*/
|
|
5316
|
-
// eslint-disable-next-line complexity
|
|
6173
|
+
/** Reads a range and returns a Response for legacy ReadableFile consumers. */
|
|
5317
6174
|
async fetchRange(offset, length4, signal) {
|
|
5318
|
-
const
|
|
5319
|
-
const
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
const
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
if (offset === 0) {
|
|
5345
|
-
const contentRange = response.headers.get("Content-Range");
|
|
5346
|
-
if (!contentRange || !contentRange.startsWith("bytes *")) {
|
|
5347
|
-
throw Error("Missing content-length on 416 response");
|
|
5348
|
-
}
|
|
5349
|
-
const actualLength = Number(contentRange.substr(8));
|
|
5350
|
-
response = await fetch(this.url, {
|
|
5351
|
-
signal,
|
|
5352
|
-
headers: { Range: `bytes=0-${actualLength - 1}` }
|
|
5353
|
-
});
|
|
5354
|
-
}
|
|
5355
|
-
break;
|
|
5356
|
-
default:
|
|
5357
|
-
if (response.status >= 300) {
|
|
5358
|
-
throw Error(`Bad response code: ${response.status}`);
|
|
6175
|
+
const numericOffset = normalizeOffset(offset);
|
|
6176
|
+
const arrayBuffer = await this.read(numericOffset, length4, signal);
|
|
6177
|
+
const headers = createIdentityHeaders(this.identity, numericOffset, length4);
|
|
6178
|
+
return new Response(arrayBuffer, { status: 206, headers });
|
|
6179
|
+
}
|
|
6180
|
+
/** Stable scheduler callback used by every read from this file. */
|
|
6181
|
+
fetchScheduledRange = async (offset, length4, signal) => {
|
|
6182
|
+
const identity4 = await this.getIdentity(signal);
|
|
6183
|
+
const result = await this.transport.requestRange(offset, length4, signal, identity4);
|
|
6184
|
+
return {
|
|
6185
|
+
arrayBuffer: result.arrayBuffer,
|
|
6186
|
+
status: result.status,
|
|
6187
|
+
transportBytes: result.transportBytes,
|
|
6188
|
+
networkTimeMs: result.networkTimeMs
|
|
6189
|
+
};
|
|
6190
|
+
};
|
|
6191
|
+
/** Returns the shared lazy identity promise, retrying after a failed probe. */
|
|
6192
|
+
getIdentity(signal) {
|
|
6193
|
+
if (this.identity) {
|
|
6194
|
+
return Promise.resolve(this.identity);
|
|
6195
|
+
}
|
|
6196
|
+
if (!this.openPromise) {
|
|
6197
|
+
const nextOpenPromise = this.probeIdentity();
|
|
6198
|
+
const cachedOpenPromise = nextOpenPromise.catch((error) => {
|
|
6199
|
+
if (this.openPromise === cachedOpenPromise) {
|
|
6200
|
+
this.openPromise = null;
|
|
5359
6201
|
}
|
|
6202
|
+
throw error;
|
|
6203
|
+
});
|
|
6204
|
+
this.openPromise = cachedOpenPromise;
|
|
6205
|
+
}
|
|
6206
|
+
return waitForPromiseWithSignal(this.openPromise, signal).catch((error) => {
|
|
6207
|
+
this.transport.trackFailure(error);
|
|
6208
|
+
throw error;
|
|
6209
|
+
});
|
|
6210
|
+
}
|
|
6211
|
+
/** Uses a one-byte GET range to discover object length and validators. */
|
|
6212
|
+
async probeIdentity() {
|
|
6213
|
+
try {
|
|
6214
|
+
const result = await this.transport.requestRange(0, 1, void 0, null);
|
|
6215
|
+
this.identity = result.identity;
|
|
6216
|
+
return result.identity;
|
|
6217
|
+
} catch (error) {
|
|
6218
|
+
this.transport.trackFailure(error);
|
|
6219
|
+
throw error;
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
/** Throws when the file has been closed. */
|
|
6223
|
+
assertOpen() {
|
|
6224
|
+
if (this.closed) {
|
|
6225
|
+
throw new Error("HttpFile is closed");
|
|
5360
6226
|
}
|
|
5361
|
-
return response;
|
|
5362
6227
|
}
|
|
5363
6228
|
};
|
|
6229
|
+
function createIdentityHeaders(identity4, offset, length4) {
|
|
6230
|
+
const headers = new Headers();
|
|
6231
|
+
if (length4 > 0 && identity4) {
|
|
6232
|
+
headers.set("Content-Range", `bytes ${offset}-${offset + length4 - 1}/${identity4.byteLength}`);
|
|
6233
|
+
}
|
|
6234
|
+
if (identity4?.etag) {
|
|
6235
|
+
headers.set("ETag", identity4.etag);
|
|
6236
|
+
}
|
|
6237
|
+
if (identity4?.lastModified) {
|
|
6238
|
+
headers.set("Last-Modified", identity4.lastModified);
|
|
6239
|
+
}
|
|
6240
|
+
return headers;
|
|
6241
|
+
}
|
|
6242
|
+
function normalizeOffset(offset) {
|
|
6243
|
+
const numericOffset = Number(offset);
|
|
6244
|
+
if (!Number.isSafeInteger(numericOffset) || numericOffset < 0) {
|
|
6245
|
+
throw new Error("HTTP byte-range offset must be a non-negative safe integer");
|
|
6246
|
+
}
|
|
6247
|
+
return numericOffset;
|
|
6248
|
+
}
|
|
6249
|
+
function validateRange(offset, length4, byteLength) {
|
|
6250
|
+
if (!Number.isSafeInteger(length4) || length4 < 0) {
|
|
6251
|
+
throw new Error("HTTP byte-range length must be a non-negative safe integer");
|
|
6252
|
+
}
|
|
6253
|
+
if (offset + length4 > byteLength) {
|
|
6254
|
+
throw new Error("HTTP byte range extends beyond the end of the file");
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
function createAbortError2() {
|
|
6258
|
+
if (typeof DOMException !== "undefined") {
|
|
6259
|
+
return new DOMException("Request aborted", "AbortError");
|
|
6260
|
+
}
|
|
6261
|
+
const error = new Error("Request aborted");
|
|
6262
|
+
error.name = "AbortError";
|
|
6263
|
+
return error;
|
|
6264
|
+
}
|
|
6265
|
+
function waitForPromiseWithSignal(promise, signal) {
|
|
6266
|
+
if (!signal) {
|
|
6267
|
+
return promise;
|
|
6268
|
+
}
|
|
6269
|
+
if (signal.aborted) {
|
|
6270
|
+
return Promise.reject(createAbortError2());
|
|
6271
|
+
}
|
|
6272
|
+
return new Promise((resolve2, reject) => {
|
|
6273
|
+
const abortListener = () => {
|
|
6274
|
+
signal.removeEventListener("abort", abortListener);
|
|
6275
|
+
reject(createAbortError2());
|
|
6276
|
+
};
|
|
6277
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
6278
|
+
promise.then(
|
|
6279
|
+
(value) => {
|
|
6280
|
+
signal.removeEventListener("abort", abortListener);
|
|
6281
|
+
resolve2(value);
|
|
6282
|
+
},
|
|
6283
|
+
(error) => {
|
|
6284
|
+
signal.removeEventListener("abort", abortListener);
|
|
6285
|
+
reject(error);
|
|
6286
|
+
}
|
|
6287
|
+
);
|
|
6288
|
+
});
|
|
6289
|
+
}
|
|
5364
6290
|
|
|
5365
6291
|
// src/utils/doubly-linked-list-node.ts
|
|
5366
6292
|
var DoublyLinkedListNode = class {
|
|
@@ -5488,14 +6414,23 @@ var __exports__ = (() => {
|
|
|
5488
6414
|
unloadCallback(tileset, tile);
|
|
5489
6415
|
}
|
|
5490
6416
|
}
|
|
6417
|
+
/**
|
|
6418
|
+
* Unloads least-recently-used tiles until the byte-native cache target is met.
|
|
6419
|
+
*
|
|
6420
|
+
* Tiles touched after the frame sentinel remain protected, so {@link Tileset3D.cacheBytes} is a
|
|
6421
|
+
* soft target rather than a hard cap on the visible working set. Overflow headroom affects
|
|
6422
|
+
* memory-adjusted SSE, not eviction of unused tiles.
|
|
6423
|
+
*
|
|
6424
|
+
* @param tileset - Tileset that owns the cache and reports estimated content memory.
|
|
6425
|
+
* @param unloadCallback - Callback that releases content and decrements the memory estimate.
|
|
6426
|
+
*/
|
|
5491
6427
|
unloadTiles(tileset, unloadCallback) {
|
|
5492
6428
|
const trimTiles = this._trimTiles;
|
|
5493
6429
|
this._trimTiles = false;
|
|
5494
6430
|
const list = this._list;
|
|
5495
|
-
const maximumMemoryUsageInBytes = tileset.maximumMemoryUsage * 1024 * 1024;
|
|
5496
6431
|
const sentinel = this._sentinel;
|
|
5497
6432
|
let node = list.head;
|
|
5498
|
-
while (node !== sentinel && (tileset.gpuMemoryUsageInBytes >
|
|
6433
|
+
while (node !== sentinel && (tileset.gpuMemoryUsageInBytes > tileset.cacheBytes || trimTiles)) {
|
|
5499
6434
|
const tile = node.item;
|
|
5500
6435
|
node = node.next;
|
|
5501
6436
|
this.unloadTile(tileset, tile, unloadCallback);
|
|
@@ -5566,12 +6501,10 @@ var __exports__ = (() => {
|
|
|
5566
6501
|
const sceneIndex = typeof gltf.scene === "number" ? gltf.scene : 0;
|
|
5567
6502
|
const scene = gltf.scenes?.[sceneIndex];
|
|
5568
6503
|
const rootNode = scene?.nodes?.[0];
|
|
5569
|
-
if (!rootNode?.matrix)
|
|
5570
|
-
return null;
|
|
6504
|
+
if (!rootNode?.matrix) return null;
|
|
5571
6505
|
const m = rootNode.matrix;
|
|
5572
6506
|
const translationMagnitude = m[12] * m[12] + m[13] * m[13] + m[14] * m[14];
|
|
5573
|
-
if (translationMagnitude <= TRANSLATION_LIMIT_SQUARED)
|
|
5574
|
-
return null;
|
|
6507
|
+
if (translationMagnitude <= TRANSLATION_LIMIT_SQUARED) return null;
|
|
5575
6508
|
return rootNode;
|
|
5576
6509
|
}
|
|
5577
6510
|
|
|
@@ -5592,7 +6525,7 @@ var __exports__ = (() => {
|
|
|
5592
6525
|
// ../../node_modules/@math.gl/culling/dist/lib/bounding-volumes/bounding-sphere.js
|
|
5593
6526
|
var scratchVector7 = new Vector3();
|
|
5594
6527
|
var scratchVector22 = new Vector3();
|
|
5595
|
-
var BoundingSphere = class {
|
|
6528
|
+
var BoundingSphere = class _BoundingSphere {
|
|
5596
6529
|
/** Creates a bounding sphere */
|
|
5597
6530
|
constructor(center = [0, 0, 0], radius = 0) {
|
|
5598
6531
|
this.radius = -0;
|
|
@@ -5621,7 +6554,7 @@ var __exports__ = (() => {
|
|
|
5621
6554
|
}
|
|
5622
6555
|
/** Duplicates a BoundingSphere instance. */
|
|
5623
6556
|
clone() {
|
|
5624
|
-
return new
|
|
6557
|
+
return new _BoundingSphere(this.center, this.radius);
|
|
5625
6558
|
}
|
|
5626
6559
|
/** Computes a bounding sphere that contains both the left and right bounding spheres. */
|
|
5627
6560
|
union(boundingSphere) {
|
|
@@ -5711,7 +6644,7 @@ var __exports__ = (() => {
|
|
|
5711
6644
|
COLUMN2ROW1: 7,
|
|
5712
6645
|
COLUMN2ROW2: 8
|
|
5713
6646
|
};
|
|
5714
|
-
var OrientedBoundingBox = class {
|
|
6647
|
+
var OrientedBoundingBox = class _OrientedBoundingBox {
|
|
5715
6648
|
constructor(center = [0, 0, 0], halfAxes = [0, 0, 0, 0, 0, 0, 0, 0, 0]) {
|
|
5716
6649
|
this.center = new Vector3().from(center);
|
|
5717
6650
|
this.halfAxes = new Matrix3(halfAxes);
|
|
@@ -5754,7 +6687,7 @@ var __exports__ = (() => {
|
|
|
5754
6687
|
}
|
|
5755
6688
|
/** Duplicates a OrientedBoundingBox instance. */
|
|
5756
6689
|
clone() {
|
|
5757
|
-
return new
|
|
6690
|
+
return new _OrientedBoundingBox(this.center, this.halfAxes);
|
|
5758
6691
|
}
|
|
5759
6692
|
/** Compares the provided OrientedBoundingBox component wise and returns */
|
|
5760
6693
|
equals(right) {
|
|
@@ -5916,7 +6849,7 @@ var __exports__ = (() => {
|
|
|
5916
6849
|
// ../../node_modules/@math.gl/culling/dist/lib/plane.js
|
|
5917
6850
|
var scratchPosition2 = new Vector3();
|
|
5918
6851
|
var scratchNormal3 = new Vector3();
|
|
5919
|
-
var Plane = class {
|
|
6852
|
+
var Plane = class _Plane {
|
|
5920
6853
|
constructor(normal = [0, 0, 1], distance2 = 0) {
|
|
5921
6854
|
this.normal = new Vector3();
|
|
5922
6855
|
this.distance = -0;
|
|
@@ -5946,7 +6879,7 @@ var __exports__ = (() => {
|
|
|
5946
6879
|
}
|
|
5947
6880
|
/** Duplicates a Plane instance. */
|
|
5948
6881
|
clone() {
|
|
5949
|
-
return new
|
|
6882
|
+
return new _Plane(this.normal, this.distance);
|
|
5950
6883
|
}
|
|
5951
6884
|
/** Compares the provided Planes by normal and distance */
|
|
5952
6885
|
equals(right) {
|
|
@@ -5976,7 +6909,7 @@ var __exports__ = (() => {
|
|
|
5976
6909
|
var faces = [new Vector3([1, 0, 0]), new Vector3([0, 1, 0]), new Vector3([0, 0, 1])];
|
|
5977
6910
|
var scratchPlaneCenter = new Vector3();
|
|
5978
6911
|
var scratchPlaneNormal = new Vector3();
|
|
5979
|
-
var CullingVolume = class {
|
|
6912
|
+
var CullingVolume = class _CullingVolume {
|
|
5980
6913
|
/**
|
|
5981
6914
|
* Create a new `CullingVolume` bounded by an array of clipping planed
|
|
5982
6915
|
* @param planes Array of clipping planes.
|
|
@@ -6037,10 +6970,10 @@ var __exports__ = (() => {
|
|
|
6037
6970
|
*/
|
|
6038
6971
|
computeVisibilityWithPlaneMask(boundingVolume, parentPlaneMask) {
|
|
6039
6972
|
assert(Number.isFinite(parentPlaneMask), "parentPlaneMask is required.");
|
|
6040
|
-
if (parentPlaneMask ===
|
|
6973
|
+
if (parentPlaneMask === _CullingVolume.MASK_OUTSIDE || parentPlaneMask === _CullingVolume.MASK_INSIDE) {
|
|
6041
6974
|
return parentPlaneMask;
|
|
6042
6975
|
}
|
|
6043
|
-
let mask =
|
|
6976
|
+
let mask = _CullingVolume.MASK_INSIDE;
|
|
6044
6977
|
const planes = this.planes;
|
|
6045
6978
|
for (let k = 0; k < this.planes.length; ++k) {
|
|
6046
6979
|
const flag = k < 31 ? 1 << k : 0;
|
|
@@ -6050,7 +6983,7 @@ var __exports__ = (() => {
|
|
|
6050
6983
|
const plane = planes[k];
|
|
6051
6984
|
const result = boundingVolume.intersectPlane(plane);
|
|
6052
6985
|
if (result === INTERSECTION.OUTSIDE) {
|
|
6053
|
-
return
|
|
6986
|
+
return _CullingVolume.MASK_OUTSIDE;
|
|
6054
6987
|
} else if (result === INTERSECTION.INTERSECTING) {
|
|
6055
6988
|
mask |= flag;
|
|
6056
6989
|
}
|
|
@@ -6105,6 +7038,8 @@ var __exports__ = (() => {
|
|
|
6105
7038
|
// src/tileset-3d/helpers/frame-state.ts
|
|
6106
7039
|
var scratchVector8 = new Vector3();
|
|
6107
7040
|
var scratchPosition3 = new Vector3();
|
|
7041
|
+
var CAMERA_POSITION_EPSILON = 1e-5;
|
|
7042
|
+
var CAMERA_DIRECTION_EPSILON = 1e-7;
|
|
6108
7043
|
var cullingVolume = new CullingVolume([
|
|
6109
7044
|
new Plane(),
|
|
6110
7045
|
new Plane(),
|
|
@@ -6113,8 +7048,9 @@ var __exports__ = (() => {
|
|
|
6113
7048
|
new Plane(),
|
|
6114
7049
|
new Plane()
|
|
6115
7050
|
]);
|
|
6116
|
-
function getFrameState(viewport, frameNumber) {
|
|
6117
|
-
const {
|
|
7051
|
+
function getFrameState(viewport, frameNumber, options = {}) {
|
|
7052
|
+
const { cameraUp, height } = viewport;
|
|
7053
|
+
const cameraDirection = viewport.cameraDirection;
|
|
6118
7054
|
const { metersPerUnit } = viewport.distanceScales;
|
|
6119
7055
|
const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
|
|
6120
7056
|
const enuToFixedTransform = Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
|
|
@@ -6123,10 +7059,18 @@ var __exports__ = (() => {
|
|
|
6123
7059
|
cameraPositionCartographic,
|
|
6124
7060
|
new Vector3()
|
|
6125
7061
|
);
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
7062
|
+
let cameraDirectionCartesian;
|
|
7063
|
+
if (cameraDirection) {
|
|
7064
|
+
cameraDirectionCartesian = new Vector3(
|
|
7065
|
+
// @ts-ignore
|
|
7066
|
+
enuToFixedTransform.transformAsVector(new Vector3(cameraDirection).scale(metersPerUnit))
|
|
7067
|
+
).normalize();
|
|
7068
|
+
} else {
|
|
7069
|
+
const nearPlane = viewport.getFrustumPlanes().near;
|
|
7070
|
+
const nearCenterCommon = closestPointOnPlane(nearPlane, viewport.cameraPosition);
|
|
7071
|
+
const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
|
|
7072
|
+
cameraDirectionCartesian = new Vector3(nearCenterCartesian).subtract(cameraPositionCartesian2).normalize();
|
|
7073
|
+
}
|
|
6130
7074
|
const cameraUpCartesian = new Vector3(
|
|
6131
7075
|
// @ts-ignore
|
|
6132
7076
|
enuToFixedTransform.transformAsVector(new Vector3(cameraUp).scale(metersPerUnit))
|
|
@@ -6147,7 +7091,10 @@ var __exports__ = (() => {
|
|
|
6147
7091
|
camera: {
|
|
6148
7092
|
position: cameraPositionCartesian2,
|
|
6149
7093
|
direction: cameraDirectionCartesian,
|
|
6150
|
-
up: cameraUpCartesian
|
|
7094
|
+
up: cameraUpCartesian,
|
|
7095
|
+
cartographicPosition: cameraPositionCartographic,
|
|
7096
|
+
verticalFieldOfView: Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0 ? Number(viewport.fovy) * Math.PI / 180 : Math.PI / 3,
|
|
7097
|
+
timeSinceMovement: options.timeSinceCameraMovement ?? Number.POSITIVE_INFINITY
|
|
6151
7098
|
},
|
|
6152
7099
|
viewport,
|
|
6153
7100
|
topDownViewport,
|
|
@@ -6155,8 +7102,29 @@ var __exports__ = (() => {
|
|
|
6155
7102
|
cullingVolume,
|
|
6156
7103
|
frameNumber,
|
|
6157
7104
|
// TODO: This can be the same between updates, what number is unique for between updates?
|
|
6158
|
-
sseDenominator: 1.15
|
|
7105
|
+
sseDenominator: 1.15,
|
|
6159
7106
|
// Assumes fovy = 60 degrees
|
|
7107
|
+
// Tileset3D fills this immediately before traversal because it depends on the root volume.
|
|
7108
|
+
dynamicScreenSpaceErrorDensity: 0
|
|
7109
|
+
};
|
|
7110
|
+
}
|
|
7111
|
+
function updateCameraMotionState(previousState, position, direction, currentTime) {
|
|
7112
|
+
const positionArray = Array.from(position);
|
|
7113
|
+
const directionArray = Array.from(direction);
|
|
7114
|
+
const positionChanged = previousState ? positionArray.length !== previousState.position.length || positionArray.some(
|
|
7115
|
+
(value, index) => Math.abs(value - previousState.position[index]) > CAMERA_POSITION_EPSILON
|
|
7116
|
+
) : false;
|
|
7117
|
+
const directionChanged = previousState ? directionArray.length !== previousState.direction.length || directionArray.some(
|
|
7118
|
+
(value, index) => Math.abs(value - previousState.direction[index]) > CAMERA_DIRECTION_EPSILON
|
|
7119
|
+
) : false;
|
|
7120
|
+
const lastMovementTime = positionChanged || directionChanged ? currentTime : previousState?.lastMovementTime ?? Number.NEGATIVE_INFINITY;
|
|
7121
|
+
return {
|
|
7122
|
+
state: {
|
|
7123
|
+
position: positionArray,
|
|
7124
|
+
direction: directionArray,
|
|
7125
|
+
lastMovementTime
|
|
7126
|
+
},
|
|
7127
|
+
timeSinceMovement: Math.max((currentTime - lastMovementTime) / 1e3, 0)
|
|
6160
7128
|
};
|
|
6161
7129
|
}
|
|
6162
7130
|
function limitSelectedTiles(tiles, frameState, maximumTilesSelected) {
|
|
@@ -6217,6 +7185,290 @@ var __exports__ = (() => {
|
|
|
6217
7185
|
return Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
|
|
6218
7186
|
}
|
|
6219
7187
|
|
|
7188
|
+
// src/tileset-3d/helpers/tiles-3d-lod.ts
|
|
7189
|
+
var scratchPositionNormal = new Vector3();
|
|
7190
|
+
var scratchCartographic = new Vector3();
|
|
7191
|
+
var scratchCenter = new Vector3();
|
|
7192
|
+
var scratchPosition4 = new Vector3();
|
|
7193
|
+
var scratchDirection = new Vector3();
|
|
7194
|
+
var scratchBoxXAxis = new Vector3();
|
|
7195
|
+
var scratchBoxYAxis = new Vector3();
|
|
7196
|
+
var scratchBoxZAxis = new Vector3();
|
|
7197
|
+
var scratchBoxQuaternion = new Quaternion();
|
|
7198
|
+
var unitZDirection = new Vector3(0, 0, 1);
|
|
7199
|
+
function updateRootTransformForDynamicScreenSpaceError(root, modelMatrix) {
|
|
7200
|
+
root._updateTransform(modelMatrix);
|
|
7201
|
+
}
|
|
7202
|
+
function calculateDynamicScreenSpaceErrorDensity(root, frameState, options) {
|
|
7203
|
+
if (!Number.isFinite(options.dynamicScreenSpaceErrorDensity) || options.dynamicScreenSpaceErrorDensity <= 0) {
|
|
7204
|
+
return 0;
|
|
7205
|
+
}
|
|
7206
|
+
const boundingVolumeHeader = getContentBoundingVolumeHeader(root);
|
|
7207
|
+
const heightRange = boundingVolumeHeader.region ? getRegionHeightRange(boundingVolumeHeader.region, frameState) : getCartesianHeightRange(root, boundingVolumeHeader, frameState);
|
|
7208
|
+
if (!heightRange) {
|
|
7209
|
+
return 0;
|
|
7210
|
+
}
|
|
7211
|
+
const heightFalloff = clamp(options.dynamicScreenSpaceErrorHeightFalloff, 0, 1);
|
|
7212
|
+
const heightClose = heightRange.minimumHeight + (heightRange.maximumHeight - heightRange.minimumHeight) * heightFalloff;
|
|
7213
|
+
const heightSpan = heightRange.maximumHeight - heightClose;
|
|
7214
|
+
const heightPercentage = heightSpan > 0 ? clamp((heightRange.cameraHeight - heightClose) / heightSpan, 0, 1) : heightRange.cameraHeight >= heightRange.maximumHeight ? 1 : 0;
|
|
7215
|
+
const verticalAlignment = clamp(
|
|
7216
|
+
Math.abs(heightRange.cameraDirection.dot(heightRange.upDirection)),
|
|
7217
|
+
0,
|
|
7218
|
+
1
|
|
7219
|
+
);
|
|
7220
|
+
const horizonFactor = (1 - verticalAlignment) * (1 - heightPercentage);
|
|
7221
|
+
return options.dynamicScreenSpaceErrorDensity * horizonFactor;
|
|
7222
|
+
}
|
|
7223
|
+
function getDynamicScreenSpaceErrorFog(distanceToCamera, density) {
|
|
7224
|
+
const scalar = distanceToCamera * density;
|
|
7225
|
+
return 1 - Math.exp(-(scalar * scalar));
|
|
7226
|
+
}
|
|
7227
|
+
function getDynamicScreenSpaceError(distanceToCamera, density, factor) {
|
|
7228
|
+
if (density <= 0 || factor <= 0 || !Number.isFinite(density) || !Number.isFinite(factor)) {
|
|
7229
|
+
return 0;
|
|
7230
|
+
}
|
|
7231
|
+
return getDynamicScreenSpaceErrorFog(distanceToCamera, density) * factor;
|
|
7232
|
+
}
|
|
7233
|
+
function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric, viewportHeightFraction = 1) {
|
|
7234
|
+
const tileset = tile.tileset;
|
|
7235
|
+
const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
|
|
7236
|
+
const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
|
|
7237
|
+
if (lodMetricValue === 0) {
|
|
7238
|
+
return 0;
|
|
7239
|
+
}
|
|
7240
|
+
const { viewDistanceScale } = tileset.options;
|
|
7241
|
+
const lodScale = viewDistanceScale || 1;
|
|
7242
|
+
const heightFraction = Number.isFinite(viewportHeightFraction) ? Math.max(viewportHeightFraction, 0) : 1;
|
|
7243
|
+
const orthographicError = getOrthographicScreenSpaceError(
|
|
7244
|
+
lodMetricValue,
|
|
7245
|
+
frameState,
|
|
7246
|
+
lodScale,
|
|
7247
|
+
heightFraction
|
|
7248
|
+
);
|
|
7249
|
+
if (orthographicError !== null) {
|
|
7250
|
+
return orthographicError;
|
|
7251
|
+
}
|
|
7252
|
+
const distanceToCamera = Math.max(tile._distanceToCamera, 1e-7);
|
|
7253
|
+
const { height, sseDenominator } = frameState;
|
|
7254
|
+
let screenSpaceError = lodMetricValue * height * heightFraction * lodScale / (distanceToCamera * sseDenominator);
|
|
7255
|
+
if (tileset.options.dynamicScreenSpaceError && !frameState.viewport.orthographic) {
|
|
7256
|
+
screenSpaceError -= getDynamicScreenSpaceError(
|
|
7257
|
+
distanceToCamera,
|
|
7258
|
+
frameState.dynamicScreenSpaceErrorDensity,
|
|
7259
|
+
tileset.options.dynamicScreenSpaceErrorFactor
|
|
7260
|
+
);
|
|
7261
|
+
}
|
|
7262
|
+
return screenSpaceError;
|
|
7263
|
+
}
|
|
7264
|
+
function getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale, viewportHeightFraction) {
|
|
7265
|
+
const viewport = frameState.viewport;
|
|
7266
|
+
if (!viewport?.orthographic) {
|
|
7267
|
+
return null;
|
|
7268
|
+
}
|
|
7269
|
+
const metersPerPixel = viewport.metersPerPixel;
|
|
7270
|
+
if (!Number.isFinite(metersPerPixel) || !metersPerPixel || metersPerPixel <= 0) {
|
|
7271
|
+
return null;
|
|
7272
|
+
}
|
|
7273
|
+
return lodMetricValue * lodScale * viewportHeightFraction / metersPerPixel;
|
|
7274
|
+
}
|
|
7275
|
+
function getContentBoundingVolumeHeader(root) {
|
|
7276
|
+
return root.header.content?.boundingVolume || root.header.boundingVolume;
|
|
7277
|
+
}
|
|
7278
|
+
function getRegionHeightRange(region, frameState) {
|
|
7279
|
+
const upDirection = scratchPositionNormal.copy(frameState.camera.position).normalize();
|
|
7280
|
+
const cameraDirection = scratchDirection.copy(frameState.camera.direction).normalize();
|
|
7281
|
+
return {
|
|
7282
|
+
cameraHeight: frameState.camera.cartographicPosition[2],
|
|
7283
|
+
minimumHeight: region[4],
|
|
7284
|
+
maximumHeight: region[5],
|
|
7285
|
+
cameraDirection,
|
|
7286
|
+
upDirection
|
|
7287
|
+
};
|
|
7288
|
+
}
|
|
7289
|
+
function getCartesianHeightRange(root, boundingVolumeHeader, frameState) {
|
|
7290
|
+
const halfHeight = getBoundingVolumeCenterAndHalfHeight(boundingVolumeHeader, scratchCenter);
|
|
7291
|
+
if (halfHeight === null) {
|
|
7292
|
+
return null;
|
|
7293
|
+
}
|
|
7294
|
+
const localTransform = new Matrix4(root.computedTransform).invert();
|
|
7295
|
+
const ellipsoid = root.tileset.ellipsoid;
|
|
7296
|
+
const centerMagnitude = Math.hypot(scratchCenter[0], scratchCenter[1], scratchCenter[2]);
|
|
7297
|
+
if (centerMagnitude > ellipsoid.minimumRadius) {
|
|
7298
|
+
const centerCartographic = ellipsoid.cartesianToCartographic(
|
|
7299
|
+
scratchCenter,
|
|
7300
|
+
scratchCartographic
|
|
7301
|
+
);
|
|
7302
|
+
const upDirection = scratchPositionNormal.copy(frameState.camera.position).normalize();
|
|
7303
|
+
const cameraDirection = scratchDirection.copy(frameState.camera.direction).normalize();
|
|
7304
|
+
return {
|
|
7305
|
+
cameraHeight: frameState.camera.cartographicPosition[2],
|
|
7306
|
+
minimumHeight: 0,
|
|
7307
|
+
maximumHeight: centerCartographic[2] * 2,
|
|
7308
|
+
cameraDirection,
|
|
7309
|
+
upDirection
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
localTransform.transformAsPoint(frameState.camera.position, scratchPosition4);
|
|
7313
|
+
localTransform.transformAsVector(frameState.camera.direction, scratchDirection);
|
|
7314
|
+
scratchDirection.normalize();
|
|
7315
|
+
return {
|
|
7316
|
+
cameraHeight: scratchPosition4[2],
|
|
7317
|
+
minimumHeight: scratchCenter[2] - halfHeight,
|
|
7318
|
+
maximumHeight: scratchCenter[2] + halfHeight,
|
|
7319
|
+
cameraDirection: scratchDirection,
|
|
7320
|
+
upDirection: unitZDirection
|
|
7321
|
+
};
|
|
7322
|
+
}
|
|
7323
|
+
function getBoundingVolumeCenterAndHalfHeight(boundingVolumeHeader, result) {
|
|
7324
|
+
if (boundingVolumeHeader.box) {
|
|
7325
|
+
const box = boundingVolumeHeader.box;
|
|
7326
|
+
result.set(box[0], box[1], box[2]);
|
|
7327
|
+
return getBoxHalfHeight(box);
|
|
7328
|
+
}
|
|
7329
|
+
if (boundingVolumeHeader.sphere) {
|
|
7330
|
+
const sphere = boundingVolumeHeader.sphere;
|
|
7331
|
+
result.set(sphere[0], sphere[1], sphere[2]);
|
|
7332
|
+
return sphere[3];
|
|
7333
|
+
}
|
|
7334
|
+
return null;
|
|
7335
|
+
}
|
|
7336
|
+
function getBoxHalfHeight(box) {
|
|
7337
|
+
if (box.length === 10) {
|
|
7338
|
+
scratchBoxQuaternion.fromArray(box, 6);
|
|
7339
|
+
scratchBoxXAxis.set(box[3], 0, 0).transformByQuaternion(scratchBoxQuaternion);
|
|
7340
|
+
scratchBoxYAxis.set(0, box[4], 0).transformByQuaternion(scratchBoxQuaternion);
|
|
7341
|
+
scratchBoxZAxis.set(0, 0, box[5]).transformByQuaternion(scratchBoxQuaternion);
|
|
7342
|
+
return Math.abs(scratchBoxXAxis[2]) + Math.abs(scratchBoxYAxis[2]) + Math.abs(scratchBoxZAxis[2]);
|
|
7343
|
+
}
|
|
7344
|
+
return Math.abs(box[5]) + Math.abs(box[8]) + Math.abs(box[11]);
|
|
7345
|
+
}
|
|
7346
|
+
|
|
7347
|
+
// src/constants.ts
|
|
7348
|
+
var TILE_CONTENT_STATE = {
|
|
7349
|
+
UNLOADED: 0,
|
|
7350
|
+
// Has never been requested
|
|
7351
|
+
LOADING: 1,
|
|
7352
|
+
// Is waiting on a pending request
|
|
7353
|
+
PROCESSING: 2,
|
|
7354
|
+
// Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
|
|
7355
|
+
READY: 3,
|
|
7356
|
+
// Ready to render.
|
|
7357
|
+
EXPIRED: 4,
|
|
7358
|
+
// Is expired and will be unloaded once new content is loaded.
|
|
7359
|
+
FAILED: 5
|
|
7360
|
+
// Request failed.
|
|
7361
|
+
};
|
|
7362
|
+
var TILE_REFINEMENT = /* @__PURE__ */ ((TILE_REFINEMENT2) => {
|
|
7363
|
+
TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
|
|
7364
|
+
TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
|
|
7365
|
+
return TILE_REFINEMENT2;
|
|
7366
|
+
})(TILE_REFINEMENT || {});
|
|
7367
|
+
var TILE_TYPE = /* @__PURE__ */ ((TILE_TYPE2) => {
|
|
7368
|
+
TILE_TYPE2["EMPTY"] = "empty";
|
|
7369
|
+
TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
|
|
7370
|
+
TILE_TYPE2["POINTCLOUD"] = "pointcloud";
|
|
7371
|
+
TILE_TYPE2["MESH"] = "mesh";
|
|
7372
|
+
return TILE_TYPE2;
|
|
7373
|
+
})(TILE_TYPE || {});
|
|
7374
|
+
var TILESET_TYPE = /* @__PURE__ */ ((TILESET_TYPE2) => {
|
|
7375
|
+
TILESET_TYPE2["I3S"] = "I3S";
|
|
7376
|
+
TILESET_TYPE2["TILES3D"] = "TILES3D";
|
|
7377
|
+
return TILESET_TYPE2;
|
|
7378
|
+
})(TILESET_TYPE || {});
|
|
7379
|
+
var LOD_METRIC_TYPE = /* @__PURE__ */ ((LOD_METRIC_TYPE2) => {
|
|
7380
|
+
LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
|
|
7381
|
+
LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
|
|
7382
|
+
return LOD_METRIC_TYPE2;
|
|
7383
|
+
})(LOD_METRIC_TYPE || {});
|
|
7384
|
+
var TILE3D_OPTIMIZATION_HINT = {
|
|
7385
|
+
NOT_COMPUTED: -1,
|
|
7386
|
+
USE_OPTIMIZATION: 1,
|
|
7387
|
+
SKIP_OPTIMIZATION: 0
|
|
7388
|
+
};
|
|
7389
|
+
|
|
7390
|
+
// src/tileset-3d/helpers/tiles-3d-request-priority.ts
|
|
7391
|
+
var PREFERRED_SORTING_DIGITS = 1e4;
|
|
7392
|
+
var FOVEATED_SORTING_SCALE = 1e4;
|
|
7393
|
+
var PROGRESSIVE_RESOLUTION_PENALTY = 1e8;
|
|
7394
|
+
var scratchBoundingSphere = new BoundingSphere();
|
|
7395
|
+
var scratchCenterOffset = new Vector3();
|
|
7396
|
+
var scratchClosestPointOnLine = new Vector3();
|
|
7397
|
+
var scratchVectorToLine = new Vector3();
|
|
7398
|
+
var scratchClosestPointOnSphere = new Vector3();
|
|
7399
|
+
function interpolateLinearly(minimumValue, maximumValue, interpolationAmount) {
|
|
7400
|
+
return minimumValue + (maximumValue - minimumValue) * interpolationAmount;
|
|
7401
|
+
}
|
|
7402
|
+
function calculateFoveatedFactor(boundingVolume, camera) {
|
|
7403
|
+
const boundingSphere = boundingVolume instanceof BoundingSphere ? boundingVolume : boundingVolume.getBoundingSphere(scratchBoundingSphere);
|
|
7404
|
+
const { center, radius } = boundingSphere;
|
|
7405
|
+
scratchCenterOffset.copy(center).subtract(camera.position);
|
|
7406
|
+
const centerZDepth = Math.max(scratchCenterOffset.dot(camera.direction), 0);
|
|
7407
|
+
scratchClosestPointOnLine.copy(camera.direction).scale(centerZDepth).add(camera.position);
|
|
7408
|
+
scratchVectorToLine.copy(scratchClosestPointOnLine).subtract(center);
|
|
7409
|
+
const distanceToCenterLine = scratchVectorToLine.magnitude();
|
|
7410
|
+
if (distanceToCenterLine <= radius) {
|
|
7411
|
+
return 0;
|
|
7412
|
+
}
|
|
7413
|
+
scratchClosestPointOnSphere.copy(scratchVectorToLine).normalize().scale(radius).add(center).subtract(camera.position);
|
|
7414
|
+
if (scratchClosestPointOnSphere.magnitude() === 0) {
|
|
7415
|
+
return 0;
|
|
7416
|
+
}
|
|
7417
|
+
scratchClosestPointOnSphere.normalize();
|
|
7418
|
+
return Math.min(Math.max(1 - scratchClosestPointOnSphere.dot(camera.direction), 0), 1);
|
|
7419
|
+
}
|
|
7420
|
+
function isProgressiveResolutionPriority(screenSpaceError, parentScreenSpaceError, maximumScreenSpaceError, progressiveResolutionHeightFraction) {
|
|
7421
|
+
if (!Number.isFinite(progressiveResolutionHeightFraction) || progressiveResolutionHeightFraction <= 0 || progressiveResolutionHeightFraction > 0.5) {
|
|
7422
|
+
return false;
|
|
7423
|
+
}
|
|
7424
|
+
if (screenSpaceError > maximumScreenSpaceError) {
|
|
7425
|
+
return true;
|
|
7426
|
+
}
|
|
7427
|
+
return parentScreenSpaceError !== void 0 && parentScreenSpaceError > maximumScreenSpaceError;
|
|
7428
|
+
}
|
|
7429
|
+
function isFoveatedRequestDeferred(parameters) {
|
|
7430
|
+
const coneSize = Number.isFinite(parameters.foveatedConeSize) ? Math.min(Math.max(parameters.foveatedConeSize, 0), 1) : 1;
|
|
7431
|
+
const replacementRefinement = parameters.refinement === 2 /* REPLACE */;
|
|
7432
|
+
if (replacementRefinement && !parameters.skipLevelOfDetail || !parameters.foveatedScreenSpaceError || coneSize >= 1 || replacementRefinement && parameters.skipLevelOfDetail && parameters.priorityProgressiveResolution) {
|
|
7433
|
+
return false;
|
|
7434
|
+
}
|
|
7435
|
+
const verticalFieldOfView = Number.isFinite(parameters.verticalFieldOfView) && parameters.verticalFieldOfView > 0 ? parameters.verticalFieldOfView : Math.PI / 3;
|
|
7436
|
+
const maximumFoveatedFactor = 1 - Math.cos(verticalFieldOfView * 0.5);
|
|
7437
|
+
const coneFactor = coneSize * maximumFoveatedFactor;
|
|
7438
|
+
if (parameters.foveatedFactor <= coneFactor || maximumFoveatedFactor <= coneFactor) {
|
|
7439
|
+
return false;
|
|
7440
|
+
}
|
|
7441
|
+
const interpolationAmount = Math.min(
|
|
7442
|
+
Math.max((parameters.foveatedFactor - coneFactor) / (maximumFoveatedFactor - coneFactor), 0),
|
|
7443
|
+
1
|
|
7444
|
+
);
|
|
7445
|
+
const interpolationCallback = typeof parameters.interpolationCallback === "function" ? parameters.interpolationCallback : interpolateLinearly;
|
|
7446
|
+
const interpolatedRelaxation = interpolationCallback(
|
|
7447
|
+
parameters.minimumScreenSpaceErrorRelaxation,
|
|
7448
|
+
parameters.maximumScreenSpaceError,
|
|
7449
|
+
interpolationAmount
|
|
7450
|
+
);
|
|
7451
|
+
const screenSpaceErrorRelaxation = Number.isFinite(interpolatedRelaxation) ? Math.min(Math.max(interpolatedRelaxation, 0), parameters.maximumScreenSpaceError) : 0;
|
|
7452
|
+
const screenSpaceError = parameters.screenSpaceError === 0 && parameters.parentScreenSpaceError !== void 0 ? parameters.parentScreenSpaceError * 0.5 : parameters.screenSpaceError;
|
|
7453
|
+
return parameters.maximumScreenSpaceError - screenSpaceErrorRelaxation <= screenSpaceError;
|
|
7454
|
+
}
|
|
7455
|
+
function isFoveatedRequestDelayActive(deferralEligible, timeSinceMovement, foveatedTimeDelay) {
|
|
7456
|
+
return deferralEligible && Number.isFinite(timeSinceMovement) && Number.isFinite(foveatedTimeDelay) && foveatedTimeDelay > 0 && timeSinceMovement < foveatedTimeDelay;
|
|
7457
|
+
}
|
|
7458
|
+
function calculateTileRequestPriority(parameters) {
|
|
7459
|
+
const normalizedReverseScreenSpaceError = parameters.rootScreenSpaceError > 0 ? Math.min(
|
|
7460
|
+
Math.max(parameters.reverseScreenSpaceError / parameters.rootScreenSpaceError, 0),
|
|
7461
|
+
1
|
|
7462
|
+
) : 0;
|
|
7463
|
+
const preferredSortingDigits = Math.floor(
|
|
7464
|
+
normalizedReverseScreenSpaceError * (PREFERRED_SORTING_DIGITS - 1)
|
|
7465
|
+
);
|
|
7466
|
+
const normalizedFoveatedFactor = Number.isFinite(parameters.foveatedFactor) ? Math.min(Math.max(parameters.foveatedFactor, 0), 1) : 0;
|
|
7467
|
+
const foveatedSortingDigits = Math.floor(normalizedFoveatedFactor * (PREFERRED_SORTING_DIGITS - 1)) * FOVEATED_SORTING_SCALE;
|
|
7468
|
+
const progressiveResolutionDigits = parameters.priorityProgressiveResolution ? 0 : PROGRESSIVE_RESOLUTION_PENALTY;
|
|
7469
|
+
return progressiveResolutionDigits + foveatedSortingDigits + preferredSortingDigits;
|
|
7470
|
+
}
|
|
7471
|
+
|
|
6220
7472
|
// src/tileset-3d/common/tileset-source.ts
|
|
6221
7473
|
function isTileset3DSource(value) {
|
|
6222
7474
|
return Boolean(
|
|
@@ -6225,14 +7477,35 @@ var __exports__ = (() => {
|
|
|
6225
7477
|
}
|
|
6226
7478
|
|
|
6227
7479
|
// src/tileset-3d/common/tileset-3d.ts
|
|
6228
|
-
var
|
|
7480
|
+
var BYTES_PER_MEBIBYTE = 1024 * 1024;
|
|
7481
|
+
var DEFAULT_CACHE_BYTES = 512 * BYTES_PER_MEBIBYTE;
|
|
7482
|
+
var DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES = 512 * BYTES_PER_MEBIBYTE;
|
|
7483
|
+
var DEFAULT_I3S_CACHE_BYTES = 32 * BYTES_PER_MEBIBYTE;
|
|
7484
|
+
var DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES = BYTES_PER_MEBIBYTE;
|
|
7485
|
+
function validateCacheByteLength(value, optionName) {
|
|
7486
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
7487
|
+
throw new Error(`${optionName} must be a finite number greater than or equal to 0`);
|
|
7488
|
+
}
|
|
7489
|
+
return value;
|
|
7490
|
+
}
|
|
7491
|
+
function resolveCacheBytes(options, fallbackCacheBytes) {
|
|
7492
|
+
const cacheBytes = options.cacheBytes ?? (options.maximumMemoryUsage !== void 0 ? options.maximumMemoryUsage * BYTES_PER_MEBIBYTE : fallbackCacheBytes);
|
|
7493
|
+
return validateCacheByteLength(cacheBytes, "cacheBytes");
|
|
7494
|
+
}
|
|
7495
|
+
function resolveMaximumCacheOverflowBytes(options, fallbackOverflowBytes) {
|
|
7496
|
+
const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? (options.memoryCacheOverflow !== void 0 ? options.memoryCacheOverflow * BYTES_PER_MEBIBYTE : fallbackOverflowBytes);
|
|
7497
|
+
return validateCacheByteLength(maximumCacheOverflowBytes, "maximumCacheOverflowBytes");
|
|
7498
|
+
}
|
|
7499
|
+
var DEFAULT_PROPS3 = {
|
|
6229
7500
|
description: "",
|
|
6230
7501
|
ellipsoid: Ellipsoid.WGS84,
|
|
6231
7502
|
modelMatrix: new Matrix4(),
|
|
6232
7503
|
throttleRequests: true,
|
|
6233
7504
|
maxRequests: 64,
|
|
6234
|
-
|
|
6235
|
-
|
|
7505
|
+
cacheBytes: DEFAULT_CACHE_BYTES,
|
|
7506
|
+
maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
|
|
7507
|
+
maximumMemoryUsage: DEFAULT_CACHE_BYTES / BYTES_PER_MEBIBYTE,
|
|
7508
|
+
memoryCacheOverflow: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES / BYTES_PER_MEBIBYTE,
|
|
6236
7509
|
maximumTilesSelected: 0,
|
|
6237
7510
|
debounceTime: 0,
|
|
6238
7511
|
onTileLoad: () => {
|
|
@@ -6250,8 +7523,18 @@ var __exports__ = (() => {
|
|
|
6250
7523
|
},
|
|
6251
7524
|
contentLoader: void 0,
|
|
6252
7525
|
viewDistanceScale: 1,
|
|
7526
|
+
progressiveResolutionHeightFraction: 0.3,
|
|
7527
|
+
foveatedScreenSpaceError: true,
|
|
7528
|
+
foveatedConeSize: 0.1,
|
|
7529
|
+
foveatedMinimumScreenSpaceErrorRelaxation: 0,
|
|
7530
|
+
foveatedInterpolationCallback: interpolateLinearly,
|
|
7531
|
+
foveatedTimeDelay: 0.2,
|
|
6253
7532
|
maximumScreenSpaceError: 8,
|
|
6254
|
-
|
|
7533
|
+
dynamicScreenSpaceError: true,
|
|
7534
|
+
dynamicScreenSpaceErrorDensity: 2e-4,
|
|
7535
|
+
dynamicScreenSpaceErrorFactor: 24,
|
|
7536
|
+
dynamicScreenSpaceErrorHeightFalloff: 0.25,
|
|
7537
|
+
memoryAdjustedScreenSpaceError: true,
|
|
6255
7538
|
loadTiles: true,
|
|
6256
7539
|
updateTransforms: true,
|
|
6257
7540
|
viewportTraversersMap: null,
|
|
@@ -6300,12 +7583,14 @@ var __exports__ = (() => {
|
|
|
6300
7583
|
cartesianCenter = null;
|
|
6301
7584
|
zoom = 1;
|
|
6302
7585
|
boundingVolume = null;
|
|
7586
|
+
/** Effective dynamic SSE density calculated for the most recently traversed viewport. */
|
|
6303
7587
|
dynamicScreenSpaceErrorComputedDensity = 0;
|
|
6304
|
-
|
|
7588
|
+
/** Estimated bytes currently retained by loaded tile content. */
|
|
6305
7589
|
gpuMemoryUsageInBytes = 0;
|
|
7590
|
+
/** Active SSE threshold after optional cache-pressure adjustment, in logical/CSS pixels. */
|
|
6306
7591
|
memoryAdjustedScreenSpaceError = 0;
|
|
6307
|
-
_cacheBytes =
|
|
6308
|
-
|
|
7592
|
+
_cacheBytes = DEFAULT_CACHE_BYTES;
|
|
7593
|
+
_maximumCacheOverflowBytes = DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES;
|
|
6309
7594
|
_frameNumber = 0;
|
|
6310
7595
|
_tiles = {};
|
|
6311
7596
|
_pendingCount = 0;
|
|
@@ -6316,6 +7601,10 @@ var __exports__ = (() => {
|
|
|
6316
7601
|
_requestedTiles = [];
|
|
6317
7602
|
_emptyTiles = [];
|
|
6318
7603
|
frameStateData = {};
|
|
7604
|
+
/** Last camera pose retained independently for each viewport. */
|
|
7605
|
+
_cameraMotionStates = {};
|
|
7606
|
+
/** Pending traversal that releases requests after camera motion settles. */
|
|
7607
|
+
_deferredTraversalTimeout = null;
|
|
6319
7608
|
_traverser;
|
|
6320
7609
|
_cache = new TilesetCache();
|
|
6321
7610
|
_requestScheduler;
|
|
@@ -6328,11 +7617,28 @@ var __exports__ = (() => {
|
|
|
6328
7617
|
* @param options Traversal and runtime options.
|
|
6329
7618
|
*/
|
|
6330
7619
|
constructor(source, options) {
|
|
6331
|
-
this.options = { ...DEFAULT_PROPS2, ...options };
|
|
6332
|
-
this.loadOptions = this.options.loadOptions || {};
|
|
6333
7620
|
if (!isTileset3DSource(source)) {
|
|
6334
7621
|
throw new Error("Tileset3D requires a Tileset3DSource instance");
|
|
6335
7622
|
}
|
|
7623
|
+
const suppliedOptions = options || {};
|
|
7624
|
+
const usesTiles3DCacheDefaults = source.type === "TILES3D" /* TILES3D */;
|
|
7625
|
+
const defaultCacheBytes = usesTiles3DCacheDefaults ? DEFAULT_PROPS3.cacheBytes : DEFAULT_I3S_CACHE_BYTES;
|
|
7626
|
+
const defaultMaximumCacheOverflowBytes = usesTiles3DCacheDefaults ? DEFAULT_PROPS3.maximumCacheOverflowBytes : DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES;
|
|
7627
|
+
const cacheBytes = resolveCacheBytes(suppliedOptions, defaultCacheBytes);
|
|
7628
|
+
const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(
|
|
7629
|
+
suppliedOptions,
|
|
7630
|
+
defaultMaximumCacheOverflowBytes
|
|
7631
|
+
);
|
|
7632
|
+
this.options = {
|
|
7633
|
+
...DEFAULT_PROPS3,
|
|
7634
|
+
...suppliedOptions,
|
|
7635
|
+
cacheBytes,
|
|
7636
|
+
maximumCacheOverflowBytes,
|
|
7637
|
+
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
7638
|
+
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE,
|
|
7639
|
+
memoryAdjustedScreenSpaceError: suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
|
|
7640
|
+
};
|
|
7641
|
+
this.loadOptions = this.options.loadOptions || {};
|
|
6336
7642
|
this.source = source;
|
|
6337
7643
|
this.tileset = null;
|
|
6338
7644
|
this.loader = this.source.loader;
|
|
@@ -6351,13 +7657,15 @@ var __exports__ = (() => {
|
|
|
6351
7657
|
maxRequests: this.options.maxRequests
|
|
6352
7658
|
});
|
|
6353
7659
|
this.memoryAdjustedScreenSpaceError = this.options.maximumScreenSpaceError;
|
|
6354
|
-
this._cacheBytes =
|
|
6355
|
-
this.
|
|
7660
|
+
this._cacheBytes = cacheBytes;
|
|
7661
|
+
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
|
|
6356
7662
|
this.stats = new Stats({ id: this.url });
|
|
6357
7663
|
this._initializeStats();
|
|
6358
7664
|
this.tilesetInitializationPromise = this._initializeTileSet();
|
|
6359
7665
|
}
|
|
6360
7666
|
destroy() {
|
|
7667
|
+
this._clearDeferredTraversal();
|
|
7668
|
+
this.source.destroy?.();
|
|
6361
7669
|
this._destroy();
|
|
6362
7670
|
}
|
|
6363
7671
|
isLoaded() {
|
|
@@ -6369,13 +7677,76 @@ var __exports__ = (() => {
|
|
|
6369
7677
|
get frameNumber() {
|
|
6370
7678
|
return this._frameNumber;
|
|
6371
7679
|
}
|
|
7680
|
+
/**
|
|
7681
|
+
* Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
|
|
7682
|
+
*
|
|
7683
|
+
* Current-frame tiles remain protected even when their estimated memory exceeds this target.
|
|
7684
|
+
* Reducing the value makes unused least-recently-used tiles eligible for eviction on the next
|
|
7685
|
+
* traversal.
|
|
7686
|
+
* @default 536870912 for 3D Tiles; 33554432 for I3S
|
|
7687
|
+
*/
|
|
7688
|
+
get cacheBytes() {
|
|
7689
|
+
return this._cacheBytes;
|
|
7690
|
+
}
|
|
7691
|
+
set cacheBytes(value) {
|
|
7692
|
+
this._cacheBytes = validateCacheByteLength(value, "cacheBytes");
|
|
7693
|
+
this.options.cacheBytes = this._cacheBytes;
|
|
7694
|
+
this.options.maximumMemoryUsage = this._cacheBytes / BYTES_PER_MEBIBYTE;
|
|
7695
|
+
}
|
|
7696
|
+
/**
|
|
7697
|
+
* Gets or sets the additional current-frame headroom, in bytes, before memory-adjusted SSE rises.
|
|
7698
|
+
*
|
|
7699
|
+
* The pressure ceiling is `cacheBytes + maximumCacheOverflowBytes`. This value does not change
|
|
7700
|
+
* the base target used to evict tiles that were not touched in the current frame.
|
|
7701
|
+
* @default 536870912 for 3D Tiles; 1048576 for I3S
|
|
7702
|
+
*/
|
|
7703
|
+
get maximumCacheOverflowBytes() {
|
|
7704
|
+
return this._maximumCacheOverflowBytes;
|
|
7705
|
+
}
|
|
7706
|
+
set maximumCacheOverflowBytes(value) {
|
|
7707
|
+
this._maximumCacheOverflowBytes = validateCacheByteLength(value, "maximumCacheOverflowBytes");
|
|
7708
|
+
this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
|
|
7709
|
+
this.options.memoryCacheOverflow = this._maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE;
|
|
7710
|
+
}
|
|
7711
|
+
/**
|
|
7712
|
+
* Gets or sets the soft cache target in mebibytes.
|
|
7713
|
+
* @deprecated Use {@link Tileset3D.cacheBytes}. Assignments remain synchronized for compatibility.
|
|
7714
|
+
*/
|
|
7715
|
+
get maximumMemoryUsage() {
|
|
7716
|
+
return this.cacheBytes / BYTES_PER_MEBIBYTE;
|
|
7717
|
+
}
|
|
7718
|
+
set maximumMemoryUsage(value) {
|
|
7719
|
+
this.cacheBytes = validateCacheByteLength(value * BYTES_PER_MEBIBYTE, "maximumMemoryUsage");
|
|
7720
|
+
}
|
|
6372
7721
|
get queryParams() {
|
|
6373
7722
|
const rootUrl = this.source.getTileUrl(this.url);
|
|
6374
7723
|
const queryIndex = rootUrl.indexOf("?");
|
|
6375
7724
|
return queryIndex >= 0 ? rootUrl.slice(queryIndex + 1) : "";
|
|
6376
7725
|
}
|
|
7726
|
+
/**
|
|
7727
|
+
* Updates traversal and runtime options, synchronizing byte-native and deprecated cache values.
|
|
7728
|
+
*
|
|
7729
|
+
* For each budget, the byte-native option takes precedence when both unit forms are supplied.
|
|
7730
|
+
* Unspecified budgets retain their current byte values, avoiding repeated MiB conversion.
|
|
7731
|
+
*
|
|
7732
|
+
* @param props - Partial runtime options to apply.
|
|
7733
|
+
*/
|
|
6377
7734
|
setProps(props) {
|
|
6378
|
-
|
|
7735
|
+
const cacheBytes = resolveCacheBytes(props, this.cacheBytes);
|
|
7736
|
+
const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(
|
|
7737
|
+
props,
|
|
7738
|
+
this.maximumCacheOverflowBytes
|
|
7739
|
+
);
|
|
7740
|
+
this.options = {
|
|
7741
|
+
...this.options,
|
|
7742
|
+
...props,
|
|
7743
|
+
cacheBytes,
|
|
7744
|
+
maximumCacheOverflowBytes,
|
|
7745
|
+
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
7746
|
+
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE
|
|
7747
|
+
};
|
|
7748
|
+
this._cacheBytes = cacheBytes;
|
|
7749
|
+
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
|
|
6379
7750
|
}
|
|
6380
7751
|
getTileUrl(tilePath) {
|
|
6381
7752
|
return this.source.getTileUrl(tilePath);
|
|
@@ -6413,13 +7784,20 @@ var __exports__ = (() => {
|
|
|
6413
7784
|
}
|
|
6414
7785
|
return this.updatePromise;
|
|
6415
7786
|
}
|
|
7787
|
+
/**
|
|
7788
|
+
* Moves the active LOD threshold toward the configured SSE or away from cache pressure.
|
|
7789
|
+
*
|
|
7790
|
+
* Usage below {@link Tileset3D.cacheBytes} restores quality in two-percent steps. Usage above
|
|
7791
|
+
* `cacheBytes + maximumCacheOverflowBytes` reduces refinement in two-percent steps. Values in
|
|
7792
|
+
* the overflow window leave the threshold unchanged, preventing rapid load/evict oscillation.
|
|
7793
|
+
*/
|
|
6416
7794
|
adjustScreenSpaceError() {
|
|
6417
|
-
if (this.gpuMemoryUsageInBytes < this.
|
|
7795
|
+
if (this.gpuMemoryUsageInBytes < this.cacheBytes) {
|
|
6418
7796
|
this.memoryAdjustedScreenSpaceError = Math.max(
|
|
6419
7797
|
this.memoryAdjustedScreenSpaceError / 1.02,
|
|
6420
7798
|
this.options.maximumScreenSpaceError
|
|
6421
7799
|
);
|
|
6422
|
-
} else if (this.gpuMemoryUsageInBytes > this.
|
|
7800
|
+
} else if (this.gpuMemoryUsageInBytes > this.cacheBytes + this.maximumCacheOverflowBytes) {
|
|
6423
7801
|
this.memoryAdjustedScreenSpaceError *= 1.02;
|
|
6424
7802
|
}
|
|
6425
7803
|
}
|
|
@@ -6434,6 +7812,8 @@ var __exports__ = (() => {
|
|
|
6434
7812
|
this._cache.reset();
|
|
6435
7813
|
this._frameNumber++;
|
|
6436
7814
|
this.traverseCounter = preparedViewports.length;
|
|
7815
|
+
const currentTime = Date.now();
|
|
7816
|
+
let minimumDeferredTraversalDelay = Number.POSITIVE_INFINITY;
|
|
6437
7817
|
const viewportsToTraverse = [];
|
|
6438
7818
|
for (const viewport of preparedViewports) {
|
|
6439
7819
|
const id = viewport.id;
|
|
@@ -6452,7 +7832,58 @@ var __exports__ = (() => {
|
|
|
6452
7832
|
continue;
|
|
6453
7833
|
}
|
|
6454
7834
|
const frameState = getFrameState(viewport, this._frameNumber);
|
|
6455
|
-
this.
|
|
7835
|
+
const root = this.roots[id];
|
|
7836
|
+
const cameraMotionUpdate = updateCameraMotionState(
|
|
7837
|
+
this._cameraMotionStates[id],
|
|
7838
|
+
frameState.camera.position,
|
|
7839
|
+
frameState.camera.direction,
|
|
7840
|
+
currentTime
|
|
7841
|
+
);
|
|
7842
|
+
this._cameraMotionStates[id] = cameraMotionUpdate.state;
|
|
7843
|
+
frameState.camera.timeSinceMovement = cameraMotionUpdate.timeSinceMovement;
|
|
7844
|
+
updateRootTransformForDynamicScreenSpaceError(root, this.modelMatrix);
|
|
7845
|
+
frameState.dynamicScreenSpaceErrorDensity = this.type === "TILES3D" /* TILES3D */ && this.options.dynamicScreenSpaceError && !frameState.viewport.orthographic ? calculateDynamicScreenSpaceErrorDensity(root, frameState, this.options) : 0;
|
|
7846
|
+
this.dynamicScreenSpaceErrorComputedDensity = frameState.dynamicScreenSpaceErrorDensity;
|
|
7847
|
+
this._traverser.traverse(root, frameState, this.options);
|
|
7848
|
+
if (Object.keys(this._traverser.deferredTiles).length > 0) {
|
|
7849
|
+
minimumDeferredTraversalDelay = Math.min(
|
|
7850
|
+
minimumDeferredTraversalDelay,
|
|
7851
|
+
Math.max(this.options.foveatedTimeDelay - cameraMotionUpdate.timeSinceMovement, 0)
|
|
7852
|
+
);
|
|
7853
|
+
}
|
|
7854
|
+
}
|
|
7855
|
+
if (Number.isFinite(minimumDeferredTraversalDelay)) {
|
|
7856
|
+
this._scheduleDeferredTraversal(minimumDeferredTraversalDelay);
|
|
7857
|
+
} else {
|
|
7858
|
+
this._clearDeferredTraversal();
|
|
7859
|
+
}
|
|
7860
|
+
}
|
|
7861
|
+
/**
|
|
7862
|
+
* Schedules one follow-up traversal when the moving-camera deferral window expires.
|
|
7863
|
+
*
|
|
7864
|
+
* Replacing an existing timer ensures continuing camera movement pushes the retry window forward
|
|
7865
|
+
* instead of creating a queue of redundant traversals.
|
|
7866
|
+
*
|
|
7867
|
+
* @param delayInSeconds - Remaining stationary delay before deferred requests become eligible.
|
|
7868
|
+
*/
|
|
7869
|
+
_scheduleDeferredTraversal(delayInSeconds) {
|
|
7870
|
+
this._clearDeferredTraversal();
|
|
7871
|
+
this._deferredTraversalTimeout = setTimeout(
|
|
7872
|
+
() => {
|
|
7873
|
+
this._deferredTraversalTimeout = null;
|
|
7874
|
+
this.selectTiles().catch((error) => {
|
|
7875
|
+
const traversalError = error instanceof Error ? error : new Error(String(error));
|
|
7876
|
+
this._onTilesetError(traversalError);
|
|
7877
|
+
});
|
|
7878
|
+
},
|
|
7879
|
+
Math.max(delayInSeconds * 1e3, 0)
|
|
7880
|
+
);
|
|
7881
|
+
}
|
|
7882
|
+
/** Clears a pending moving-camera follow-up traversal. */
|
|
7883
|
+
_clearDeferredTraversal() {
|
|
7884
|
+
if (this._deferredTraversalTimeout !== null) {
|
|
7885
|
+
clearTimeout(this._deferredTraversalTimeout);
|
|
7886
|
+
this._deferredTraversalTimeout = null;
|
|
6456
7887
|
}
|
|
6457
7888
|
}
|
|
6458
7889
|
_needTraverse(viewportId) {
|
|
@@ -6506,8 +7937,7 @@ var __exports__ = (() => {
|
|
|
6506
7937
|
this._heldTiles.add(tileId);
|
|
6507
7938
|
}
|
|
6508
7939
|
for (const tileId of this._heldTiles) {
|
|
6509
|
-
if (selectedIds.has(tileId))
|
|
6510
|
-
continue;
|
|
7940
|
+
if (selectedIds.has(tileId)) continue;
|
|
6511
7941
|
const tile = this._tiles[tileId];
|
|
6512
7942
|
if (tile && tile.contentAvailable) {
|
|
6513
7943
|
tile._selectedFrame = this._frameNumber;
|
|
@@ -6555,6 +7985,9 @@ var __exports__ = (() => {
|
|
|
6555
7985
|
}
|
|
6556
7986
|
_unloadTiles() {
|
|
6557
7987
|
this._cache.unloadTiles(this, (tileset, tile) => tileset._unloadTile(tile));
|
|
7988
|
+
if (this.options.memoryAdjustedScreenSpaceError) {
|
|
7989
|
+
this.adjustScreenSpaceError();
|
|
7990
|
+
}
|
|
6558
7991
|
}
|
|
6559
7992
|
_updateStats() {
|
|
6560
7993
|
let tilesRenderable = 0;
|
|
@@ -6638,6 +8071,27 @@ var __exports__ = (() => {
|
|
|
6638
8071
|
this._onTileLoad(tile, loadResult);
|
|
6639
8072
|
}
|
|
6640
8073
|
}
|
|
8074
|
+
/**
|
|
8075
|
+
* Loads a source-managed lazy child-header group and reports failures through tile callbacks.
|
|
8076
|
+
*
|
|
8077
|
+
* Subtree metadata shares the normal loading counters but is not reported as render content and
|
|
8078
|
+
* is not added to the GPU cache. Traversal is notified separately after the promise settles.
|
|
8079
|
+
*
|
|
8080
|
+
* @param tile - Runtime tile that owns the lazy subtree reference.
|
|
8081
|
+
* @param frameState - View state that made the subtree eligible for refinement.
|
|
8082
|
+
*/
|
|
8083
|
+
async _loadTileChildren(tile, frameState) {
|
|
8084
|
+
try {
|
|
8085
|
+
this._onStartTileLoading();
|
|
8086
|
+
await tile.loadChildren(frameState);
|
|
8087
|
+
} catch (error) {
|
|
8088
|
+
const tileError = error instanceof Error ? error : new Error("subtree load failed");
|
|
8089
|
+
this._onSourceError(tileError, tile);
|
|
8090
|
+
this._onTileLoadError(tile, tileError);
|
|
8091
|
+
} finally {
|
|
8092
|
+
this._onEndTileLoading();
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
6641
8095
|
_onTileLoadError(tile, error) {
|
|
6642
8096
|
this.stats.get(TILES_LOAD_FAILED).incrementCount();
|
|
6643
8097
|
const message = error.message || error.toString();
|
|
@@ -6898,49 +8352,6 @@ var __exports__ = (() => {
|
|
|
6898
8352
|
return queryIndex >= 0 ? url.slice(0, queryIndex) : url;
|
|
6899
8353
|
}
|
|
6900
8354
|
|
|
6901
|
-
// src/constants.ts
|
|
6902
|
-
var TILE_CONTENT_STATE = {
|
|
6903
|
-
UNLOADED: 0,
|
|
6904
|
-
// Has never been requested
|
|
6905
|
-
LOADING: 1,
|
|
6906
|
-
// Is waiting on a pending request
|
|
6907
|
-
PROCESSING: 2,
|
|
6908
|
-
// Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
|
|
6909
|
-
READY: 3,
|
|
6910
|
-
// Ready to render.
|
|
6911
|
-
EXPIRED: 4,
|
|
6912
|
-
// Is expired and will be unloaded once new content is loaded.
|
|
6913
|
-
FAILED: 5
|
|
6914
|
-
// Request failed.
|
|
6915
|
-
};
|
|
6916
|
-
var TILE_REFINEMENT = /* @__PURE__ */ ((TILE_REFINEMENT2) => {
|
|
6917
|
-
TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
|
|
6918
|
-
TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
|
|
6919
|
-
return TILE_REFINEMENT2;
|
|
6920
|
-
})(TILE_REFINEMENT || {});
|
|
6921
|
-
var TILE_TYPE = /* @__PURE__ */ ((TILE_TYPE2) => {
|
|
6922
|
-
TILE_TYPE2["EMPTY"] = "empty";
|
|
6923
|
-
TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
|
|
6924
|
-
TILE_TYPE2["POINTCLOUD"] = "pointcloud";
|
|
6925
|
-
TILE_TYPE2["MESH"] = "mesh";
|
|
6926
|
-
return TILE_TYPE2;
|
|
6927
|
-
})(TILE_TYPE || {});
|
|
6928
|
-
var TILESET_TYPE = /* @__PURE__ */ ((TILESET_TYPE2) => {
|
|
6929
|
-
TILESET_TYPE2["I3S"] = "I3S";
|
|
6930
|
-
TILESET_TYPE2["TILES3D"] = "TILES3D";
|
|
6931
|
-
return TILESET_TYPE2;
|
|
6932
|
-
})(TILESET_TYPE || {});
|
|
6933
|
-
var LOD_METRIC_TYPE = /* @__PURE__ */ ((LOD_METRIC_TYPE2) => {
|
|
6934
|
-
LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
|
|
6935
|
-
LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
|
|
6936
|
-
return LOD_METRIC_TYPE2;
|
|
6937
|
-
})(LOD_METRIC_TYPE || {});
|
|
6938
|
-
var TILE3D_OPTIMIZATION_HINT = {
|
|
6939
|
-
NOT_COMPUTED: -1,
|
|
6940
|
-
USE_OPTIMIZATION: 1,
|
|
6941
|
-
SKIP_OPTIMIZATION: 0
|
|
6942
|
-
};
|
|
6943
|
-
|
|
6944
8355
|
// src/tileset-3d/helpers/bounding-volume.ts
|
|
6945
8356
|
function defined(x) {
|
|
6946
8357
|
return x !== void 0 && x !== null;
|
|
@@ -6949,7 +8360,7 @@ var __exports__ = (() => {
|
|
|
6949
8360
|
var scratchScale = new Vector3();
|
|
6950
8361
|
var scratchNorthWest = new Vector3();
|
|
6951
8362
|
var scratchSouthEast = new Vector3();
|
|
6952
|
-
var
|
|
8363
|
+
var scratchCenter2 = new Vector3();
|
|
6953
8364
|
var scratchXAxis = new Vector3();
|
|
6954
8365
|
var scratchYAxis = new Vector3();
|
|
6955
8366
|
var scratchZAxis = new Vector3();
|
|
@@ -7048,17 +8459,17 @@ var __exports__ = (() => {
|
|
|
7048
8459
|
scratchSouthEast
|
|
7049
8460
|
);
|
|
7050
8461
|
const centerInCartesian = new Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5);
|
|
7051
|
-
Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian,
|
|
8462
|
+
Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter2);
|
|
7052
8463
|
Ellipsoid.WGS84.cartographicToCartesian(
|
|
7053
|
-
[degrees(east),
|
|
8464
|
+
[degrees(east), scratchCenter2[1], scratchCenter2[2]],
|
|
7054
8465
|
scratchXAxis
|
|
7055
8466
|
);
|
|
7056
8467
|
Ellipsoid.WGS84.cartographicToCartesian(
|
|
7057
|
-
[
|
|
8468
|
+
[scratchCenter2[0], degrees(north), scratchCenter2[2]],
|
|
7058
8469
|
scratchYAxis
|
|
7059
8470
|
);
|
|
7060
8471
|
Ellipsoid.WGS84.cartographicToCartesian(
|
|
7061
|
-
[
|
|
8472
|
+
[scratchCenter2[0], scratchCenter2[1], maxHeight],
|
|
7062
8473
|
scratchZAxis
|
|
7063
8474
|
);
|
|
7064
8475
|
return createBox(
|
|
@@ -7137,57 +8548,6 @@ var __exports__ = (() => {
|
|
|
7137
8548
|
target[1][2] = Math.max(target[1][2], scratchPoint[2]);
|
|
7138
8549
|
}
|
|
7139
8550
|
|
|
7140
|
-
// src/tileset-3d/helpers/tiles-3d-lod.ts
|
|
7141
|
-
var scratchPositionNormal = new Vector3();
|
|
7142
|
-
var scratchCartographic = new Vector3();
|
|
7143
|
-
var scratchMatrix2 = new Matrix4();
|
|
7144
|
-
var scratchCenter2 = new Vector3();
|
|
7145
|
-
var scratchPosition4 = new Vector3();
|
|
7146
|
-
var scratchDirection = new Vector3();
|
|
7147
|
-
function fog(distanceToCamera, density) {
|
|
7148
|
-
const scalar = distanceToCamera * density;
|
|
7149
|
-
return 1 - Math.exp(-(scalar * scalar));
|
|
7150
|
-
}
|
|
7151
|
-
function getDynamicScreenSpaceError(tileset, distanceToCamera) {
|
|
7152
|
-
if (tileset.dynamicScreenSpaceError && tileset.dynamicScreenSpaceErrorComputedDensity) {
|
|
7153
|
-
const density = tileset.dynamicScreenSpaceErrorComputedDensity;
|
|
7154
|
-
const factor = tileset.dynamicScreenSpaceErrorFactor;
|
|
7155
|
-
const dynamicError = fog(distanceToCamera, density) * factor;
|
|
7156
|
-
return dynamicError;
|
|
7157
|
-
}
|
|
7158
|
-
return 0;
|
|
7159
|
-
}
|
|
7160
|
-
function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric) {
|
|
7161
|
-
const tileset = tile.tileset;
|
|
7162
|
-
const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
|
|
7163
|
-
const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
|
|
7164
|
-
if (lodMetricValue === 0) {
|
|
7165
|
-
return 0;
|
|
7166
|
-
}
|
|
7167
|
-
const { viewDistanceScale } = tileset.options;
|
|
7168
|
-
const lodScale = viewDistanceScale || 1;
|
|
7169
|
-
const orthographicError = getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale);
|
|
7170
|
-
if (orthographicError !== null) {
|
|
7171
|
-
return orthographicError;
|
|
7172
|
-
}
|
|
7173
|
-
const distance2 = Math.max(tile._distanceToCamera, 1e-7);
|
|
7174
|
-
const { height, sseDenominator } = frameState;
|
|
7175
|
-
let error = lodMetricValue * height * lodScale / (distance2 * sseDenominator);
|
|
7176
|
-
error -= getDynamicScreenSpaceError(tileset, distance2);
|
|
7177
|
-
return error;
|
|
7178
|
-
}
|
|
7179
|
-
function getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale) {
|
|
7180
|
-
const viewport = frameState.viewport;
|
|
7181
|
-
if (!viewport?.orthographic) {
|
|
7182
|
-
return null;
|
|
7183
|
-
}
|
|
7184
|
-
const metersPerPixel = viewport.metersPerPixel;
|
|
7185
|
-
if (!Number.isFinite(metersPerPixel) || metersPerPixel <= 0) {
|
|
7186
|
-
return null;
|
|
7187
|
-
}
|
|
7188
|
-
return lodMetricValue * lodScale / metersPerPixel;
|
|
7189
|
-
}
|
|
7190
|
-
|
|
7191
8551
|
// src/tileset-3d/helpers/i3s-lod.ts
|
|
7192
8552
|
var cameraPositionCartesian = new Vector3();
|
|
7193
8553
|
var toEye = new Vector3();
|
|
@@ -7385,7 +8745,7 @@ var __exports__ = (() => {
|
|
|
7385
8745
|
};
|
|
7386
8746
|
|
|
7387
8747
|
// src/tileset-3d/common/tileset-traverser.ts
|
|
7388
|
-
var
|
|
8748
|
+
var DEFAULT_PROPS4 = {
|
|
7389
8749
|
loadSiblings: false,
|
|
7390
8750
|
skipLevelOfDetail: false,
|
|
7391
8751
|
updateTransforms: true,
|
|
@@ -7402,9 +8762,11 @@ var __exports__ = (() => {
|
|
|
7402
8762
|
selectedTiles = {};
|
|
7403
8763
|
// tiles should be loaded from server
|
|
7404
8764
|
requestedTiles = {};
|
|
8765
|
+
/** Tiles intentionally held until the camera has remained still for the configured delay. */
|
|
8766
|
+
deferredTiles = {};
|
|
7405
8767
|
// tiles does not have render content
|
|
7406
8768
|
emptyTiles = {};
|
|
7407
|
-
lastUpdate = new Date().getTime();
|
|
8769
|
+
lastUpdate = (/* @__PURE__ */ new Date()).getTime();
|
|
7408
8770
|
updateDebounceTime = 1e3;
|
|
7409
8771
|
/** temporary storage to hold the traversed tiles during a traversal */
|
|
7410
8772
|
_traversalStack = new ManagedArray();
|
|
@@ -7417,7 +8779,7 @@ var __exports__ = (() => {
|
|
|
7417
8779
|
}
|
|
7418
8780
|
// TODO nested props
|
|
7419
8781
|
constructor(options) {
|
|
7420
|
-
this.options = { ...
|
|
8782
|
+
this.options = { ...DEFAULT_PROPS4, ...options };
|
|
7421
8783
|
}
|
|
7422
8784
|
// tiles should be visible
|
|
7423
8785
|
traverse(root, frameState, options) {
|
|
@@ -7430,6 +8792,7 @@ var __exports__ = (() => {
|
|
|
7430
8792
|
}
|
|
7431
8793
|
reset() {
|
|
7432
8794
|
this.requestedTiles = {};
|
|
8795
|
+
this.deferredTiles = {};
|
|
7433
8796
|
this.selectedTiles = {};
|
|
7434
8797
|
this.emptyTiles = {};
|
|
7435
8798
|
this._traversalStack.reset();
|
|
@@ -7482,7 +8845,7 @@ var __exports__ = (() => {
|
|
|
7482
8845
|
this.touchTile(tile, frameState);
|
|
7483
8846
|
tile._shouldRefine = shouldRefine && parentRefines;
|
|
7484
8847
|
}
|
|
7485
|
-
const newTime = new Date().getTime();
|
|
8848
|
+
const newTime = (/* @__PURE__ */ new Date()).getTime();
|
|
7486
8849
|
if (this.traversalFinished(frameState) || newTime - this.lastUpdate > this.updateDebounceTime) {
|
|
7487
8850
|
this.lastUpdate = newTime;
|
|
7488
8851
|
this.options.onTraversalEnd(frameState);
|
|
@@ -7548,11 +8911,28 @@ var __exports__ = (() => {
|
|
|
7548
8911
|
// tile to load from server
|
|
7549
8912
|
loadTile(tile, frameState) {
|
|
7550
8913
|
if (this.shouldLoadTile(tile)) {
|
|
8914
|
+
if (this.shouldDeferTileRequest(tile)) {
|
|
8915
|
+
this.deferredTiles[tile.id] = tile;
|
|
8916
|
+
return;
|
|
8917
|
+
}
|
|
7551
8918
|
tile._requestedFrame = frameState.frameNumber;
|
|
7552
8919
|
tile._priority = tile._getPriority();
|
|
7553
8920
|
this.requestedTiles[tile.id] = tile;
|
|
7554
8921
|
}
|
|
7555
8922
|
}
|
|
8923
|
+
/**
|
|
8924
|
+
* Returns whether an eligible peripheral request should wait for camera motion to settle.
|
|
8925
|
+
*
|
|
8926
|
+
* Eligibility and the current time window are calculated together when tile visibility updates.
|
|
8927
|
+
* Keeping the active state on the tile also lets a queued scheduler callback cancel work that
|
|
8928
|
+
* became deferred after its original traversal.
|
|
8929
|
+
*
|
|
8930
|
+
* @param tile - Tile considered for loading.
|
|
8931
|
+
* @returns `true` when the request should be retried after the remaining delay.
|
|
8932
|
+
*/
|
|
8933
|
+
shouldDeferTileRequest(tile) {
|
|
8934
|
+
return tile.priorityDeferred;
|
|
8935
|
+
}
|
|
7556
8936
|
// cache tile
|
|
7557
8937
|
touchTile(tile, frameState) {
|
|
7558
8938
|
tile.tileset._cache.touch(tile);
|
|
@@ -7614,12 +8994,17 @@ var __exports__ = (() => {
|
|
|
7614
8994
|
// Ignores visibility.
|
|
7615
8995
|
executeEmptyTraversal(root, frameState) {
|
|
7616
8996
|
let allDescendantsLoaded = true;
|
|
8997
|
+
let hasPendingLazyChildren = false;
|
|
7617
8998
|
const stack = this._emptyTraversalStack;
|
|
7618
8999
|
stack.push(root);
|
|
7619
9000
|
while (stack.length > 0) {
|
|
7620
9001
|
const tile = stack.pop();
|
|
7621
9002
|
const traverse = !tile.hasRenderContent && this.canTraverse(tile, frameState);
|
|
7622
9003
|
const emptyLeaf = !tile.hasRenderContent && tile.children.length === 0;
|
|
9004
|
+
if (tile.header.implicitSubtree) {
|
|
9005
|
+
hasPendingLazyChildren = true;
|
|
9006
|
+
allDescendantsLoaded = false;
|
|
9007
|
+
}
|
|
7623
9008
|
if (!traverse && !tile.contentAvailable && !emptyLeaf) {
|
|
7624
9009
|
allDescendantsLoaded = false;
|
|
7625
9010
|
}
|
|
@@ -7635,7 +9020,7 @@ var __exports__ = (() => {
|
|
|
7635
9020
|
}
|
|
7636
9021
|
}
|
|
7637
9022
|
}
|
|
7638
|
-
return root.hasEmptyContent || allDescendantsLoaded;
|
|
9023
|
+
return root.hasEmptyContent && !hasPendingLazyChildren || allDescendantsLoaded;
|
|
7639
9024
|
}
|
|
7640
9025
|
};
|
|
7641
9026
|
|
|
@@ -7677,6 +9062,8 @@ var __exports__ = (() => {
|
|
|
7677
9062
|
gpuMemoryUsageInBytes = 0;
|
|
7678
9063
|
/** The tile's children - an array of Tile3D objects. */
|
|
7679
9064
|
children = [];
|
|
9065
|
+
/** Current lifecycle state for a lazy implicit subtree rooted at this tile. */
|
|
9066
|
+
childrenState = "ready";
|
|
7680
9067
|
depth = 0;
|
|
7681
9068
|
viewportIds = [];
|
|
7682
9069
|
transform = new Matrix4();
|
|
@@ -7695,10 +9082,20 @@ var __exports__ = (() => {
|
|
|
7695
9082
|
// TODO Cesium 3d tiles specific
|
|
7696
9083
|
_expireDate = null;
|
|
7697
9084
|
_expiredContent = null;
|
|
9085
|
+
/** In-flight source request shared by repeated traversal frames. */
|
|
9086
|
+
_childrenPromise = null;
|
|
7698
9087
|
_boundingBox = void 0;
|
|
7699
9088
|
/** updated every frame for tree traversal and rendering optimizations: */
|
|
7700
9089
|
_distanceToCamera = 0;
|
|
7701
9090
|
_screenSpaceError = 0;
|
|
9091
|
+
/** SSE calculated at the reduced viewport height used to prioritize coarse coverage. */
|
|
9092
|
+
_screenSpaceErrorProgressiveResolution = 0;
|
|
9093
|
+
/** Whether this tile should be promoted into the progressive-resolution request pass. */
|
|
9094
|
+
_priorityProgressiveResolution = false;
|
|
9095
|
+
/** Angular distance from the camera view axis used to prioritize center tiles. */
|
|
9096
|
+
_foveatedFactor = 0;
|
|
9097
|
+
/** Whether this peripheral request is currently inside the camera-motion deferral window. */
|
|
9098
|
+
priorityDeferred = false;
|
|
7702
9099
|
_visibilityPlaneMask;
|
|
7703
9100
|
_visible = void 0;
|
|
7704
9101
|
_contentBoundingVolume;
|
|
@@ -7743,6 +9140,7 @@ var __exports__ = (() => {
|
|
|
7743
9140
|
this.refine = this._getRefine(header.refine);
|
|
7744
9141
|
this.type = header.type;
|
|
7745
9142
|
this.contentUrl = header.contentUrl;
|
|
9143
|
+
this.childrenState = header.implicitSubtree ? "unloaded" : "ready";
|
|
7746
9144
|
this._initializeLodMetric(header);
|
|
7747
9145
|
this._initializeTransforms(header);
|
|
7748
9146
|
this._updateLodMetricScale();
|
|
@@ -7772,7 +9170,17 @@ var __exports__ = (() => {
|
|
|
7772
9170
|
}
|
|
7773
9171
|
/** Returns true if tile has children */
|
|
7774
9172
|
get hasChildren() {
|
|
7775
|
-
return this.children.length > 0 || this.header.children && this.header.children.length > 0;
|
|
9173
|
+
return this.children.length > 0 || this.header.children && this.header.children.length > 0 || Boolean(this.header.implicitSubtree && this.childrenState !== "ready");
|
|
9174
|
+
}
|
|
9175
|
+
/** Returns whether traversal may request an unresolved lazy child-header group. */
|
|
9176
|
+
get hasUnloadedChildren() {
|
|
9177
|
+
return Boolean(
|
|
9178
|
+
this.header.implicitSubtree && (this.childrenState === "unloaded" || this.childrenState === "failed")
|
|
9179
|
+
);
|
|
9180
|
+
}
|
|
9181
|
+
/** Returns whether a lazy child-header request is currently in flight. */
|
|
9182
|
+
get childrenLoading() {
|
|
9183
|
+
return this.childrenState === "loading";
|
|
7776
9184
|
}
|
|
7777
9185
|
/**
|
|
7778
9186
|
* Determines if the tile's content is ready. This is automatically `true` for
|
|
@@ -7835,13 +9243,28 @@ var __exports__ = (() => {
|
|
|
7835
9243
|
}
|
|
7836
9244
|
return this._boundingBox;
|
|
7837
9245
|
}
|
|
7838
|
-
/**
|
|
7839
|
-
|
|
9246
|
+
/**
|
|
9247
|
+
* Gets the tile's screen-space error for normal refinement or a reduced-height priority pass.
|
|
9248
|
+
*
|
|
9249
|
+
* I3S already supplies a screen-oriented LOD metric and intentionally ignores the optional 3D
|
|
9250
|
+
* Tiles progressive-resolution height fraction.
|
|
9251
|
+
*
|
|
9252
|
+
* @param frameState - Current camera and viewport measurements.
|
|
9253
|
+
* @param useParentLodMetric - Whether to substitute the parent LOD metric.
|
|
9254
|
+
* @param viewportHeightFraction - Fraction of viewport height represented by the priority pass.
|
|
9255
|
+
* @returns Screen-space LOD error for the current tileset format.
|
|
9256
|
+
*/
|
|
9257
|
+
getScreenSpaceError(frameState, useParentLodMetric, viewportHeightFraction = 1) {
|
|
7840
9258
|
switch (this.tileset.type) {
|
|
7841
9259
|
case "I3S" /* I3S */:
|
|
7842
9260
|
return getProjectedRadius(this, frameState);
|
|
7843
9261
|
case "TILES3D" /* TILES3D */:
|
|
7844
|
-
return getTiles3DScreenSpaceError(
|
|
9262
|
+
return getTiles3DScreenSpaceError(
|
|
9263
|
+
this,
|
|
9264
|
+
frameState,
|
|
9265
|
+
useParentLodMetric,
|
|
9266
|
+
viewportHeightFraction
|
|
9267
|
+
);
|
|
7845
9268
|
default:
|
|
7846
9269
|
throw new Error("Unsupported tileset type");
|
|
7847
9270
|
}
|
|
@@ -7859,12 +9282,34 @@ var __exports__ = (() => {
|
|
|
7859
9282
|
_getGpuMemoryUsageInBytes() {
|
|
7860
9283
|
return this.content.gpuMemoryUsageInBytes || this.content.byteLength || 0;
|
|
7861
9284
|
}
|
|
7862
|
-
|
|
7863
|
-
*
|
|
7864
|
-
*
|
|
9285
|
+
/**
|
|
9286
|
+
* Returns scheduler priority for render-content requests.
|
|
9287
|
+
*
|
|
9288
|
+
* @returns Numeric priority, or a negative value when the queued request should be cancelled.
|
|
7865
9289
|
*/
|
|
7866
|
-
// eslint-disable-next-line complexity
|
|
7867
9290
|
_getPriority() {
|
|
9291
|
+
return this._calculateRequestPriority(false);
|
|
9292
|
+
}
|
|
9293
|
+
/**
|
|
9294
|
+
* Returns scheduler priority for lazy child-header requests.
|
|
9295
|
+
*
|
|
9296
|
+
* Contentless implicit placeholders intentionally retain `UNLOADED` content state, so metadata
|
|
9297
|
+
* scheduling skips only that render-content cancellation check and preserves every visibility,
|
|
9298
|
+
* recency, progressive, foveated, and motion-deferral rule.
|
|
9299
|
+
*
|
|
9300
|
+
* @returns Numeric priority, or a negative value when the queued request should be cancelled.
|
|
9301
|
+
*/
|
|
9302
|
+
_getChildrenPriority() {
|
|
9303
|
+
return this._calculateRequestPriority(true);
|
|
9304
|
+
}
|
|
9305
|
+
/**
|
|
9306
|
+
* Calculates the common scheduler priority for content or hierarchy metadata.
|
|
9307
|
+
*
|
|
9308
|
+
* @param allowUnloadedContent - Whether an empty placeholder may request child metadata.
|
|
9309
|
+
* @returns Numeric priority, or a negative value when the queued request should be cancelled.
|
|
9310
|
+
*/
|
|
9311
|
+
// eslint-disable-next-line complexity
|
|
9312
|
+
_calculateRequestPriority(allowUnloadedContent) {
|
|
7868
9313
|
const traverser = this.tileset._traverser;
|
|
7869
9314
|
const { skipLevelOfDetail } = traverser.options;
|
|
7870
9315
|
const maySkipTile = this.refine === 1 /* ADD */ || skipLevelOfDetail;
|
|
@@ -7874,14 +9319,23 @@ var __exports__ = (() => {
|
|
|
7874
9319
|
if (this.tileset._frameNumber - this._touchedFrame >= 1) {
|
|
7875
9320
|
return -1;
|
|
7876
9321
|
}
|
|
7877
|
-
if (this.
|
|
9322
|
+
if (this.priorityDeferred) {
|
|
9323
|
+
return -1;
|
|
9324
|
+
}
|
|
9325
|
+
if (!allowUnloadedContent && this.contentState === TILE_CONTENT_STATE.UNLOADED) {
|
|
7878
9326
|
return -1;
|
|
7879
9327
|
}
|
|
7880
9328
|
const parent = this.parent;
|
|
7881
9329
|
const useParentScreenSpaceError = parent && (!maySkipTile || this._screenSpaceError === 0 || parent.hasTilesetContent);
|
|
7882
9330
|
const screenSpaceError = useParentScreenSpaceError ? parent._screenSpaceError : this._screenSpaceError;
|
|
7883
9331
|
const rootScreenSpaceError = traverser.root ? traverser.root._screenSpaceError : 0;
|
|
7884
|
-
|
|
9332
|
+
const reverseScreenSpaceError = Math.max(rootScreenSpaceError - screenSpaceError, 0);
|
|
9333
|
+
return calculateTileRequestPriority({
|
|
9334
|
+
priorityProgressiveResolution: this._priorityProgressiveResolution,
|
|
9335
|
+
foveatedFactor: this._foveatedFactor,
|
|
9336
|
+
reverseScreenSpaceError,
|
|
9337
|
+
rootScreenSpaceError
|
|
9338
|
+
});
|
|
7885
9339
|
}
|
|
7886
9340
|
/**
|
|
7887
9341
|
* Requests the tile's content.
|
|
@@ -7923,6 +9377,87 @@ var __exports__ = (() => {
|
|
|
7923
9377
|
requestToken.done();
|
|
7924
9378
|
}
|
|
7925
9379
|
}
|
|
9380
|
+
/**
|
|
9381
|
+
* Requests and installs this tile's lazy child-header group through its source.
|
|
9382
|
+
*
|
|
9383
|
+
* The same request scheduler and SSE/foveated priority value used for render content are reused
|
|
9384
|
+
* for subtree metadata. Repeated traversal frames share one promise, and scheduler cancellation
|
|
9385
|
+
* restores the unloaded state so a later frame can retry.
|
|
9386
|
+
*
|
|
9387
|
+
* @param frameState - View state that made the subtree eligible for refinement.
|
|
9388
|
+
* @returns Source load result, or an unloaded result if scheduling was cancelled.
|
|
9389
|
+
*/
|
|
9390
|
+
async loadChildren(frameState) {
|
|
9391
|
+
if (!this.header.implicitSubtree || this.childrenState === "ready") {
|
|
9392
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
9393
|
+
}
|
|
9394
|
+
if (this._childrenPromise) {
|
|
9395
|
+
return await this._childrenPromise;
|
|
9396
|
+
}
|
|
9397
|
+
if (!this.tileset.source.loadTileChildren) {
|
|
9398
|
+
throw new Error("Tileset source does not support lazy tile children");
|
|
9399
|
+
}
|
|
9400
|
+
this.childrenState = "loading";
|
|
9401
|
+
this._childrenPromise = this._loadChildrenWithScheduler(frameState);
|
|
9402
|
+
try {
|
|
9403
|
+
return await this._childrenPromise;
|
|
9404
|
+
} finally {
|
|
9405
|
+
this._childrenPromise = null;
|
|
9406
|
+
}
|
|
9407
|
+
}
|
|
9408
|
+
/**
|
|
9409
|
+
* Runs one lazy child-header load after obtaining a scheduler token.
|
|
9410
|
+
*
|
|
9411
|
+
* @param frameState - View state forwarded to the source implementation.
|
|
9412
|
+
* @returns Source load result.
|
|
9413
|
+
*/
|
|
9414
|
+
async _loadChildrenWithScheduler(frameState) {
|
|
9415
|
+
const requestToken = await this.tileset._requestScheduler.scheduleRequest(
|
|
9416
|
+
`${this.id}:implicit-subtree`,
|
|
9417
|
+
this._getChildrenPriority.bind(this)
|
|
9418
|
+
);
|
|
9419
|
+
if (!requestToken) {
|
|
9420
|
+
this.childrenState = "unloaded";
|
|
9421
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
9422
|
+
}
|
|
9423
|
+
try {
|
|
9424
|
+
const loadResult = await this.tileset.source.loadTileChildren(this, frameState);
|
|
9425
|
+
this.childrenState = loadResult.loaded ? "ready" : "unloaded";
|
|
9426
|
+
return loadResult;
|
|
9427
|
+
} catch (error) {
|
|
9428
|
+
this.childrenState = "failed";
|
|
9429
|
+
throw error;
|
|
9430
|
+
} finally {
|
|
9431
|
+
requestToken.done();
|
|
9432
|
+
}
|
|
9433
|
+
}
|
|
9434
|
+
/**
|
|
9435
|
+
* Replaces a contentless implicit placeholder with its materialized subtree-root header.
|
|
9436
|
+
*
|
|
9437
|
+
* Transform state is intentionally preserved: the placeholder already owns the composed
|
|
9438
|
+
* transform, and reapplying the root transform here would double-transform bounds and geometric
|
|
9439
|
+
* error. Content and raw geometric error are reinitialized from the newly available header.
|
|
9440
|
+
*
|
|
9441
|
+
* @param materializedHeader - Available root header produced from one subtree resource.
|
|
9442
|
+
*/
|
|
9443
|
+
applyImplicitSubtreeHeader(materializedHeader) {
|
|
9444
|
+
const existingTransform = this.header.transform;
|
|
9445
|
+
const existingTransformMatrix = this.header.transformMatrix;
|
|
9446
|
+
this.header = {
|
|
9447
|
+
...this.header,
|
|
9448
|
+
...materializedHeader,
|
|
9449
|
+
transform: existingTransform,
|
|
9450
|
+
transformMatrix: existingTransformMatrix,
|
|
9451
|
+
implicitSubtree: void 0
|
|
9452
|
+
};
|
|
9453
|
+
this.refine = this._getRefine(materializedHeader.refine);
|
|
9454
|
+
this.type = materializedHeader.type;
|
|
9455
|
+
this.contentUrl = materializedHeader.contentUrl;
|
|
9456
|
+
this._initializeLodMetric(materializedHeader);
|
|
9457
|
+
this._updateLodMetricScale();
|
|
9458
|
+
this._initializeBoundingVolumes(this.header);
|
|
9459
|
+
this._initializeContent(this.header);
|
|
9460
|
+
}
|
|
7926
9461
|
// Unloads the tile's content.
|
|
7927
9462
|
unloadContent() {
|
|
7928
9463
|
if (this.content && this.content.destroy) {
|
|
@@ -7955,12 +9490,69 @@ var __exports__ = (() => {
|
|
|
7955
9490
|
}
|
|
7956
9491
|
this._distanceToCamera = this.distanceToTile(frameState);
|
|
7957
9492
|
this._screenSpaceError = this.getScreenSpaceError(frameState, false);
|
|
9493
|
+
this._updateRequestPriorityMetrics(frameState);
|
|
7958
9494
|
this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask);
|
|
7959
9495
|
this._visible = this._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE;
|
|
7960
9496
|
this._inRequestVolume = this.insideViewerRequestVolume(frameState);
|
|
7961
9497
|
this._frameNumber = frameState.frameNumber;
|
|
7962
9498
|
this.viewportIds = viewportIds;
|
|
7963
9499
|
}
|
|
9500
|
+
/**
|
|
9501
|
+
* Updates progressive-resolution and foveated request metrics for a 3D Tiles traversal frame.
|
|
9502
|
+
*
|
|
9503
|
+
* I3S retains its format-specific screen-threshold ordering and is explicitly isolated from
|
|
9504
|
+
* geometric-error scheduling. Orthographic viewports still receive progressive coverage, but
|
|
9505
|
+
* angular foveation is disabled because an orthographic frustum has no perspective field of view.
|
|
9506
|
+
*
|
|
9507
|
+
* @param frameState - Current camera and viewport measurements.
|
|
9508
|
+
*/
|
|
9509
|
+
_updateRequestPriorityMetrics(frameState) {
|
|
9510
|
+
if (this.tileset.type !== "TILES3D" /* TILES3D */) {
|
|
9511
|
+
this._screenSpaceErrorProgressiveResolution = this._screenSpaceError;
|
|
9512
|
+
this._priorityProgressiveResolution = false;
|
|
9513
|
+
this._foveatedFactor = 0;
|
|
9514
|
+
this.priorityDeferred = false;
|
|
9515
|
+
return;
|
|
9516
|
+
}
|
|
9517
|
+
const options = this.tileset.options;
|
|
9518
|
+
const progressiveResolutionHeightFraction = options.progressiveResolutionHeightFraction;
|
|
9519
|
+
this._screenSpaceErrorProgressiveResolution = this.getScreenSpaceError(
|
|
9520
|
+
frameState,
|
|
9521
|
+
false,
|
|
9522
|
+
progressiveResolutionHeightFraction
|
|
9523
|
+
);
|
|
9524
|
+
this._priorityProgressiveResolution = isProgressiveResolutionPriority(
|
|
9525
|
+
this._screenSpaceErrorProgressiveResolution,
|
|
9526
|
+
this.parent?._screenSpaceErrorProgressiveResolution,
|
|
9527
|
+
this.tileset.memoryAdjustedScreenSpaceError,
|
|
9528
|
+
progressiveResolutionHeightFraction
|
|
9529
|
+
);
|
|
9530
|
+
if (!options.foveatedScreenSpaceError || frameState.viewport?.orthographic || !frameState.camera?.position || !frameState.camera?.direction) {
|
|
9531
|
+
this._foveatedFactor = 0;
|
|
9532
|
+
this.priorityDeferred = false;
|
|
9533
|
+
return;
|
|
9534
|
+
}
|
|
9535
|
+
this._foveatedFactor = calculateFoveatedFactor(this.boundingVolume, frameState.camera);
|
|
9536
|
+
const deferralEligible = isFoveatedRequestDeferred({
|
|
9537
|
+
refinement: this.refine,
|
|
9538
|
+
skipLevelOfDetail: this.tileset._traverser.options.skipLevelOfDetail,
|
|
9539
|
+
foveatedScreenSpaceError: options.foveatedScreenSpaceError,
|
|
9540
|
+
foveatedConeSize: options.foveatedConeSize,
|
|
9541
|
+
minimumScreenSpaceErrorRelaxation: options.foveatedMinimumScreenSpaceErrorRelaxation,
|
|
9542
|
+
interpolationCallback: options.foveatedInterpolationCallback,
|
|
9543
|
+
foveatedFactor: this._foveatedFactor,
|
|
9544
|
+
verticalFieldOfView: frameState.camera.verticalFieldOfView,
|
|
9545
|
+
screenSpaceError: this._screenSpaceError,
|
|
9546
|
+
parentScreenSpaceError: this.parent?._screenSpaceError,
|
|
9547
|
+
maximumScreenSpaceError: this.tileset.memoryAdjustedScreenSpaceError,
|
|
9548
|
+
priorityProgressiveResolution: this._priorityProgressiveResolution
|
|
9549
|
+
});
|
|
9550
|
+
this.priorityDeferred = isFoveatedRequestDelayActive(
|
|
9551
|
+
deferralEligible,
|
|
9552
|
+
frameState.camera.timeSinceMovement,
|
|
9553
|
+
options.foveatedTimeDelay
|
|
9554
|
+
);
|
|
9555
|
+
}
|
|
7964
9556
|
// Determines whether the tile's bounding volume intersects the culling volume.
|
|
7965
9557
|
// @param {FrameState} frameState The frame state.
|
|
7966
9558
|
// @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check.
|
|
@@ -8089,6 +9681,10 @@ var __exports__ = (() => {
|
|
|
8089
9681
|
this._distanceToCamera = 0;
|
|
8090
9682
|
this._centerZDepth = 0;
|
|
8091
9683
|
this._screenSpaceError = 0;
|
|
9684
|
+
this._screenSpaceErrorProgressiveResolution = 0;
|
|
9685
|
+
this._priorityProgressiveResolution = false;
|
|
9686
|
+
this._foveatedFactor = 0;
|
|
9687
|
+
this.priorityDeferred = false;
|
|
8092
9688
|
this._visibilityPlaneMask = CullingVolume.MASK_INDETERMINATE;
|
|
8093
9689
|
this._visible = void 0;
|
|
8094
9690
|
this._inRequestVolume = false;
|
|
@@ -8104,8 +9700,15 @@ var __exports__ = (() => {
|
|
|
8104
9700
|
_getRefine(refine) {
|
|
8105
9701
|
return refine || this.parent && this.parent.refine || 2 /* REPLACE */;
|
|
8106
9702
|
}
|
|
9703
|
+
/**
|
|
9704
|
+
* Returns whether loaded content is an external tileset.
|
|
9705
|
+
*
|
|
9706
|
+
* Parsed shape is authoritative because valid external resources may be extensionless, signed,
|
|
9707
|
+
* or use a misleading filename. This method is called only after source loading has populated
|
|
9708
|
+
* `content`.
|
|
9709
|
+
*/
|
|
8107
9710
|
_isTileset() {
|
|
8108
|
-
return this.
|
|
9711
|
+
return this.content?.shape === "tileset3d";
|
|
8109
9712
|
}
|
|
8110
9713
|
_onContentLoaded() {
|
|
8111
9714
|
switch (this.content && this.content.type) {
|
|
@@ -8127,6 +9730,13 @@ var __exports__ = (() => {
|
|
|
8127
9730
|
this.computedTransform,
|
|
8128
9731
|
this.boundingVolume
|
|
8129
9732
|
);
|
|
9733
|
+
if (header.viewerRequestVolume) {
|
|
9734
|
+
this._viewerRequestVolume = createBoundingVolume(
|
|
9735
|
+
header.viewerRequestVolume,
|
|
9736
|
+
this.computedTransform,
|
|
9737
|
+
this._viewerRequestVolume
|
|
9738
|
+
);
|
|
9739
|
+
}
|
|
8130
9740
|
const content = header.content;
|
|
8131
9741
|
if (!content) {
|
|
8132
9742
|
return;
|
|
@@ -8138,13 +9748,6 @@ var __exports__ = (() => {
|
|
|
8138
9748
|
this._contentBoundingVolume
|
|
8139
9749
|
);
|
|
8140
9750
|
}
|
|
8141
|
-
if (header.viewerRequestVolume) {
|
|
8142
|
-
this._viewerRequestVolume = createBoundingVolume(
|
|
8143
|
-
header.viewerRequestVolume,
|
|
8144
|
-
this.computedTransform,
|
|
8145
|
-
this._viewerRequestVolume
|
|
8146
|
-
);
|
|
8147
|
-
}
|
|
8148
9751
|
}
|
|
8149
9752
|
/**
|
|
8150
9753
|
* Updates the tile's computed transform, world-space geometric error, and bounding volumes.
|
|
@@ -8164,9 +9767,72 @@ var __exports__ = (() => {
|
|
|
8164
9767
|
|
|
8165
9768
|
// src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts
|
|
8166
9769
|
var Tileset3DTraverser = class extends TilesetTraverser {
|
|
9770
|
+
/** Pending implicit subtree requests keyed by viewport and tile identity. */
|
|
9771
|
+
pendingImplicitSubtrees = /* @__PURE__ */ new Map();
|
|
9772
|
+
/**
|
|
9773
|
+
* Reports whether every implicit subtree request started by this viewport traversal has settled.
|
|
9774
|
+
*
|
|
9775
|
+
* @param frameState - Traversal frame whose completion callback is being considered.
|
|
9776
|
+
* @returns `true` when no matching subtree metadata request remains pending.
|
|
9777
|
+
*/
|
|
9778
|
+
traversalFinished(frameState) {
|
|
9779
|
+
const viewportPrefix = `${frameState.viewport.id}:`;
|
|
9780
|
+
for (const [requestKey, frameNumber] of this.pendingImplicitSubtrees) {
|
|
9781
|
+
if (requestKey.startsWith(viewportPrefix) && frameNumber === frameState.frameNumber) {
|
|
9782
|
+
return false;
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
return true;
|
|
9786
|
+
}
|
|
8167
9787
|
compareDistanceToCamera(a, b) {
|
|
8168
9788
|
return b._distanceToCamera === 0 && a._distanceToCamera === 0 ? b._centerZDepth - a._centerZDepth : b._distanceToCamera - a._distanceToCamera;
|
|
8169
9789
|
}
|
|
9790
|
+
/**
|
|
9791
|
+
* Starts an implicit subtree request only after culling, request-volume, and SSE eligibility.
|
|
9792
|
+
*
|
|
9793
|
+
* The current parent remains the traversal boundary while metadata is loading, which preserves
|
|
9794
|
+
* REPLACE coverage and ADD accumulation. Request scheduling reads the tile's existing
|
|
9795
|
+
* progressive-resolution and foveated metrics, so subtree metadata follows the same priority
|
|
9796
|
+
* policy as render content. A completion notification lets the owning tileset publish the newly
|
|
9797
|
+
* materialized traversal result on the next update.
|
|
9798
|
+
*
|
|
9799
|
+
* @param tile - Tile whose existing or lazy children should be updated.
|
|
9800
|
+
* @param frameState - Current culling and LOD state.
|
|
9801
|
+
*/
|
|
9802
|
+
updateChildTiles(tile, frameState) {
|
|
9803
|
+
if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && this.shouldRefine(tile, frameState)) {
|
|
9804
|
+
const requestKey = `${frameState.viewport.id}:${tile.id}`;
|
|
9805
|
+
if (!this.pendingImplicitSubtrees.has(requestKey)) {
|
|
9806
|
+
this.pendingImplicitSubtrees.set(requestKey, frameState.frameNumber);
|
|
9807
|
+
void tile.tileset._loadTileChildren(tile, frameState).finally(() => {
|
|
9808
|
+
this.handleImplicitSubtreeLoad(tile, frameState, requestKey);
|
|
9809
|
+
});
|
|
9810
|
+
}
|
|
9811
|
+
}
|
|
9812
|
+
super.updateChildTiles(tile, frameState);
|
|
9813
|
+
}
|
|
9814
|
+
/**
|
|
9815
|
+
* Resumes the same traversal frame after one lazy subtree request settles.
|
|
9816
|
+
*
|
|
9817
|
+
* Successful materialization traverses the new headers immediately. Failed or scheduler-cancelled
|
|
9818
|
+
* requests finish the current traversal without retrying in a tight loop; a later application
|
|
9819
|
+
* update may retry them.
|
|
9820
|
+
*
|
|
9821
|
+
* @param tile - Subtree-root tile whose request settled.
|
|
9822
|
+
* @param frameState - Frame that started the request.
|
|
9823
|
+
* @param requestKey - Viewport-scoped pending-request identity.
|
|
9824
|
+
*/
|
|
9825
|
+
handleImplicitSubtreeLoad(tile, frameState, requestKey) {
|
|
9826
|
+
this.pendingImplicitSubtrees.delete(requestKey);
|
|
9827
|
+
if (this._frameNumber !== frameState.frameNumber) {
|
|
9828
|
+
return;
|
|
9829
|
+
}
|
|
9830
|
+
if (tile.childrenState === "ready") {
|
|
9831
|
+
this.executeTraversal(tile, frameState);
|
|
9832
|
+
} else if (this.traversalFinished(frameState)) {
|
|
9833
|
+
this.options.onTraversalEnd(frameState);
|
|
9834
|
+
}
|
|
9835
|
+
}
|
|
8170
9836
|
updateTileVisibility(tile, frameState) {
|
|
8171
9837
|
super.updateTileVisibility(tile, frameState);
|
|
8172
9838
|
if (!tile.isVisibleAndInRequestVolume) {
|
|
@@ -8249,6 +9915,263 @@ var __exports__ = (() => {
|
|
|
8249
9915
|
return size;
|
|
8250
9916
|
}
|
|
8251
9917
|
|
|
9918
|
+
// src/tileset-3d/format-3d-tiles/implicit-tiling.ts
|
|
9919
|
+
var QUADTREE_CHILD_COUNT = 4;
|
|
9920
|
+
var OCTREE_CHILD_COUNT = 8;
|
|
9921
|
+
function createImplicitSubtreeReference(descriptor, coordinates) {
|
|
9922
|
+
return {
|
|
9923
|
+
coordinates,
|
|
9924
|
+
subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
|
|
9925
|
+
descriptor
|
|
9926
|
+
};
|
|
9927
|
+
}
|
|
9928
|
+
function materializeImplicitSubtree(subtree, reference) {
|
|
9929
|
+
const { descriptor } = reference;
|
|
9930
|
+
validateImplicitDescriptor(descriptor);
|
|
9931
|
+
const counters = { tileCount: 0, childSubtreeCount: 0 };
|
|
9932
|
+
const root = materializeAvailableTile(
|
|
9933
|
+
subtree,
|
|
9934
|
+
reference,
|
|
9935
|
+
{ localLevel: 0, mortonIndex: 0 },
|
|
9936
|
+
reference.coordinates,
|
|
9937
|
+
counters
|
|
9938
|
+
);
|
|
9939
|
+
if (!root) {
|
|
9940
|
+
throw new Error(
|
|
9941
|
+
`Invalid 3D Tiles subtree: root tile is unavailable at ${reference.subtreeUrl}`
|
|
9942
|
+
);
|
|
9943
|
+
}
|
|
9944
|
+
return { root, ...counters };
|
|
9945
|
+
}
|
|
9946
|
+
function replaceImplicitUrlTemplate(templateUrl, coordinates) {
|
|
9947
|
+
const values = {
|
|
9948
|
+
level: coordinates.level,
|
|
9949
|
+
x: coordinates.x,
|
|
9950
|
+
y: coordinates.y,
|
|
9951
|
+
z: coordinates.z
|
|
9952
|
+
};
|
|
9953
|
+
return templateUrl.replace(
|
|
9954
|
+
/{(level|x|y|z)}/gi,
|
|
9955
|
+
(_match, coordinateName) => String(values[coordinateName.toLowerCase()])
|
|
9956
|
+
);
|
|
9957
|
+
}
|
|
9958
|
+
function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
|
|
9959
|
+
const { descriptor } = reference;
|
|
9960
|
+
const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
|
|
9961
|
+
const availabilityIndex = getLevelOffset(childCount, localCoordinates.localLevel) + localCoordinates.mortonIndex;
|
|
9962
|
+
if (!getAvailabilityValue(subtree.tileAvailability, availabilityIndex)) {
|
|
9963
|
+
return null;
|
|
9964
|
+
}
|
|
9965
|
+
counters.tileCount++;
|
|
9966
|
+
const contentAvailable = getAvailabilityValue(
|
|
9967
|
+
getPrimaryContentAvailability(subtree.contentAvailability),
|
|
9968
|
+
availabilityIndex
|
|
9969
|
+
);
|
|
9970
|
+
const children = [];
|
|
9971
|
+
if (globalCoordinates.level < descriptor.maximumLevel) {
|
|
9972
|
+
if (localCoordinates.localLevel + 1 < descriptor.subtreeLevels) {
|
|
9973
|
+
for (let childIndex = 0; childIndex < childCount; childIndex++) {
|
|
9974
|
+
const childCoordinates = getChildCoordinates(
|
|
9975
|
+
globalCoordinates,
|
|
9976
|
+
childIndex,
|
|
9977
|
+
descriptor.subdivisionScheme
|
|
9978
|
+
);
|
|
9979
|
+
const childHeader = materializeAvailableTile(
|
|
9980
|
+
subtree,
|
|
9981
|
+
reference,
|
|
9982
|
+
{
|
|
9983
|
+
localLevel: localCoordinates.localLevel + 1,
|
|
9984
|
+
mortonIndex: localCoordinates.mortonIndex * childCount + childIndex
|
|
9985
|
+
},
|
|
9986
|
+
childCoordinates,
|
|
9987
|
+
counters
|
|
9988
|
+
);
|
|
9989
|
+
if (childHeader) {
|
|
9990
|
+
children.push(childHeader);
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
} else {
|
|
9994
|
+
for (let childIndex = 0; childIndex < childCount; childIndex++) {
|
|
9995
|
+
const childSubtreeIndex = localCoordinates.mortonIndex * childCount + childIndex;
|
|
9996
|
+
if (getAvailabilityValue(subtree.childSubtreeAvailability, childSubtreeIndex)) {
|
|
9997
|
+
const childCoordinates = getChildCoordinates(
|
|
9998
|
+
globalCoordinates,
|
|
9999
|
+
childIndex,
|
|
10000
|
+
descriptor.subdivisionScheme
|
|
10001
|
+
);
|
|
10002
|
+
children.push(createLazyImplicitTileHeader(descriptor, childCoordinates));
|
|
10003
|
+
counters.childSubtreeCount++;
|
|
10004
|
+
}
|
|
10005
|
+
}
|
|
10006
|
+
}
|
|
10007
|
+
}
|
|
10008
|
+
return formatImplicitTileHeader(
|
|
10009
|
+
descriptor,
|
|
10010
|
+
reference,
|
|
10011
|
+
globalCoordinates,
|
|
10012
|
+
contentAvailable,
|
|
10013
|
+
children
|
|
10014
|
+
);
|
|
10015
|
+
}
|
|
10016
|
+
function createLazyImplicitTileHeader(descriptor, coordinates) {
|
|
10017
|
+
const reference = createImplicitSubtreeReference(descriptor, coordinates);
|
|
10018
|
+
return {
|
|
10019
|
+
id: getImplicitTileId(reference, coordinates),
|
|
10020
|
+
children: [],
|
|
10021
|
+
implicitSubtree: reference,
|
|
10022
|
+
boundingVolume: calculateImplicitBoundingVolume(
|
|
10023
|
+
descriptor.rootBoundingVolume,
|
|
10024
|
+
coordinates,
|
|
10025
|
+
descriptor.subdivisionScheme
|
|
10026
|
+
),
|
|
10027
|
+
geometricError: descriptor.rootLodMetricValue / 2 ** coordinates.level,
|
|
10028
|
+
lodMetricType: descriptor.lodMetricType,
|
|
10029
|
+
lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
|
|
10030
|
+
refine: descriptor.refine,
|
|
10031
|
+
type: "empty" /* EMPTY */
|
|
10032
|
+
};
|
|
10033
|
+
}
|
|
10034
|
+
function formatImplicitTileHeader(descriptor, reference, coordinates, contentAvailable, children) {
|
|
10035
|
+
const contentUrl = contentAvailable && descriptor.contentUrlTemplate ? replaceImplicitUrlTemplate(descriptor.contentUrlTemplate, coordinates) : void 0;
|
|
10036
|
+
const lodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
|
|
10037
|
+
return {
|
|
10038
|
+
id: getImplicitTileId(reference, coordinates),
|
|
10039
|
+
children,
|
|
10040
|
+
contentUrl,
|
|
10041
|
+
content: contentUrl ? { ...descriptor.contentHeader, uri: contentUrl } : void 0,
|
|
10042
|
+
refine: descriptor.refine,
|
|
10043
|
+
type: getImplicitTileType(contentUrl),
|
|
10044
|
+
lodMetricType: descriptor.lodMetricType,
|
|
10045
|
+
lodMetricValue,
|
|
10046
|
+
geometricError: lodMetricValue,
|
|
10047
|
+
boundingVolume: calculateImplicitBoundingVolume(
|
|
10048
|
+
descriptor.rootBoundingVolume,
|
|
10049
|
+
coordinates,
|
|
10050
|
+
descriptor.subdivisionScheme
|
|
10051
|
+
)
|
|
10052
|
+
};
|
|
10053
|
+
}
|
|
10054
|
+
function getImplicitTileId(reference, coordinates) {
|
|
10055
|
+
return `${reference.subtreeUrl}#implicit=${coordinates.level}/${coordinates.x}/${coordinates.y}/${coordinates.z}`;
|
|
10056
|
+
}
|
|
10057
|
+
function getAvailabilityValue(availability, index) {
|
|
10058
|
+
if (typeof availability?.constant === "number") {
|
|
10059
|
+
return Boolean(availability.constant);
|
|
10060
|
+
}
|
|
10061
|
+
const bitstream = availability?.explicitBitstream;
|
|
10062
|
+
if (!bitstream) {
|
|
10063
|
+
return false;
|
|
10064
|
+
}
|
|
10065
|
+
const byteIndex = Math.floor(index / 8);
|
|
10066
|
+
const bitIndex = index % 8;
|
|
10067
|
+
return Boolean(bitstream[byteIndex] >> bitIndex & 1);
|
|
10068
|
+
}
|
|
10069
|
+
function getPrimaryContentAvailability(availability) {
|
|
10070
|
+
return Array.isArray(availability) ? availability[0] || { constant: 0 } : availability || { constant: 0 };
|
|
10071
|
+
}
|
|
10072
|
+
function getLevelOffset(childCount, localLevel) {
|
|
10073
|
+
return (childCount ** localLevel - 1) / (childCount - 1);
|
|
10074
|
+
}
|
|
10075
|
+
function getChildCoordinates(parent, childIndex, subdivisionScheme) {
|
|
10076
|
+
return {
|
|
10077
|
+
level: parent.level + 1,
|
|
10078
|
+
x: parent.x * 2 + (childIndex & 1),
|
|
10079
|
+
y: parent.y * 2 + (childIndex >> 1 & 1),
|
|
10080
|
+
z: subdivisionScheme === "OCTREE" ? parent.z * 2 + (childIndex >> 2 & 1) : 0
|
|
10081
|
+
};
|
|
10082
|
+
}
|
|
10083
|
+
function getImplicitChildCount(subdivisionScheme) {
|
|
10084
|
+
return subdivisionScheme === "OCTREE" ? OCTREE_CHILD_COUNT : QUADTREE_CHILD_COUNT;
|
|
10085
|
+
}
|
|
10086
|
+
function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
|
|
10087
|
+
if (rootBoundingVolume.region) {
|
|
10088
|
+
const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
|
|
10089
|
+
const divisionCount = 2 ** coordinates.level;
|
|
10090
|
+
const longitudeSize = (east - west) / divisionCount;
|
|
10091
|
+
const latitudeSize = (north - south) / divisionCount;
|
|
10092
|
+
const childWest = west + longitudeSize * coordinates.x;
|
|
10093
|
+
const childSouth = south + latitudeSize * coordinates.y;
|
|
10094
|
+
let childMinimumHeight = minimumHeight;
|
|
10095
|
+
let childMaximumHeight = maximumHeight;
|
|
10096
|
+
if (subdivisionScheme === "OCTREE") {
|
|
10097
|
+
const heightSize = (maximumHeight - minimumHeight) / divisionCount;
|
|
10098
|
+
childMinimumHeight = minimumHeight + heightSize * coordinates.z;
|
|
10099
|
+
childMaximumHeight = childMinimumHeight + heightSize;
|
|
10100
|
+
}
|
|
10101
|
+
return {
|
|
10102
|
+
region: [
|
|
10103
|
+
childWest,
|
|
10104
|
+
childSouth,
|
|
10105
|
+
childWest + longitudeSize,
|
|
10106
|
+
childSouth + latitudeSize,
|
|
10107
|
+
childMinimumHeight,
|
|
10108
|
+
childMaximumHeight
|
|
10109
|
+
]
|
|
10110
|
+
};
|
|
10111
|
+
}
|
|
10112
|
+
if (rootBoundingVolume.box) {
|
|
10113
|
+
if (rootBoundingVolume.s2VolumeInfo) {
|
|
10114
|
+
return { box: [...rootBoundingVolume.box], s2VolumeInfo: rootBoundingVolume.s2VolumeInfo };
|
|
10115
|
+
}
|
|
10116
|
+
const box = rootBoundingVolume.box;
|
|
10117
|
+
const divisionCount = 2 ** coordinates.level;
|
|
10118
|
+
const xOffset = -1 + (2 * coordinates.x + 1) / divisionCount;
|
|
10119
|
+
const yOffset = -1 + (2 * coordinates.y + 1) / divisionCount;
|
|
10120
|
+
const zOffset = -1 + (2 * coordinates.z + 1) / divisionCount;
|
|
10121
|
+
const divideHeight = subdivisionScheme === "OCTREE";
|
|
10122
|
+
const center = [
|
|
10123
|
+
box[0] + box[3] * xOffset + box[6] * yOffset + (divideHeight ? box[9] * zOffset : 0),
|
|
10124
|
+
box[1] + box[4] * xOffset + box[7] * yOffset + (divideHeight ? box[10] * zOffset : 0),
|
|
10125
|
+
box[2] + box[5] * xOffset + box[8] * yOffset + (divideHeight ? box[11] * zOffset : 0)
|
|
10126
|
+
];
|
|
10127
|
+
return {
|
|
10128
|
+
box: [
|
|
10129
|
+
...center,
|
|
10130
|
+
box[3] / divisionCount,
|
|
10131
|
+
box[4] / divisionCount,
|
|
10132
|
+
box[5] / divisionCount,
|
|
10133
|
+
box[6] / divisionCount,
|
|
10134
|
+
box[7] / divisionCount,
|
|
10135
|
+
box[8] / divisionCount,
|
|
10136
|
+
divideHeight ? box[9] / divisionCount : box[9],
|
|
10137
|
+
divideHeight ? box[10] / divisionCount : box[10],
|
|
10138
|
+
divideHeight ? box[11] / divisionCount : box[11]
|
|
10139
|
+
]
|
|
10140
|
+
};
|
|
10141
|
+
}
|
|
10142
|
+
throw new Error(
|
|
10143
|
+
`Unsupported implicit 3D Tiles bounding volume: ${JSON.stringify(rootBoundingVolume)}`
|
|
10144
|
+
);
|
|
10145
|
+
}
|
|
10146
|
+
function getImplicitTileType(contentUrl) {
|
|
10147
|
+
if (!contentUrl) {
|
|
10148
|
+
return "empty" /* EMPTY */;
|
|
10149
|
+
}
|
|
10150
|
+
const extension = contentUrl.split("?")[0].split(".").pop()?.toLowerCase();
|
|
10151
|
+
switch (extension) {
|
|
10152
|
+
case "pnts":
|
|
10153
|
+
return "pointcloud" /* POINTCLOUD */;
|
|
10154
|
+
case "i3dm":
|
|
10155
|
+
case "b3dm":
|
|
10156
|
+
case "glb":
|
|
10157
|
+
case "gltf":
|
|
10158
|
+
return "scenegraph" /* SCENEGRAPH */;
|
|
10159
|
+
default:
|
|
10160
|
+
return extension || "empty" /* EMPTY */;
|
|
10161
|
+
}
|
|
10162
|
+
}
|
|
10163
|
+
function validateImplicitDescriptor(descriptor) {
|
|
10164
|
+
if (descriptor.subdivisionScheme !== "QUADTREE" && descriptor.subdivisionScheme !== "OCTREE") {
|
|
10165
|
+
throw new Error(`Unsupported implicit subdivision scheme: ${descriptor.subdivisionScheme}`);
|
|
10166
|
+
}
|
|
10167
|
+
if (!Number.isInteger(descriptor.subtreeLevels) || descriptor.subtreeLevels < 1) {
|
|
10168
|
+
throw new Error("Implicit subtreeLevels must be a positive integer");
|
|
10169
|
+
}
|
|
10170
|
+
if (!Number.isInteger(descriptor.maximumLevel) || descriptor.maximumLevel < 0) {
|
|
10171
|
+
throw new Error("Implicit availableLevels must describe at least the root level");
|
|
10172
|
+
}
|
|
10173
|
+
}
|
|
10174
|
+
|
|
8252
10175
|
// src/tileset-3d/format-3d-tiles/tiles-3d-source.ts
|
|
8253
10176
|
var EMPTY_CONTENT_FORMATS = {
|
|
8254
10177
|
draco: false,
|
|
@@ -8256,6 +10179,7 @@ var __exports__ = (() => {
|
|
|
8256
10179
|
dds: false,
|
|
8257
10180
|
ktx2: false
|
|
8258
10181
|
};
|
|
10182
|
+
var DEFAULT_MAXIMUM_CACHED_SUBTREES = 32;
|
|
8259
10183
|
var Tiles3DSource = class {
|
|
8260
10184
|
/** 3D Tiles format discriminator. */
|
|
8261
10185
|
type = "TILES3D" /* TILES3D */;
|
|
@@ -8284,6 +10208,23 @@ var __exports__ = (() => {
|
|
|
8284
10208
|
/** Normalized source metadata after initialization. */
|
|
8285
10209
|
metadata;
|
|
8286
10210
|
queryParams = {};
|
|
10211
|
+
/** Final request URLs cached by unmodified tile content URL. */
|
|
10212
|
+
tileUrlCache = /* @__PURE__ */ new Map();
|
|
10213
|
+
/** Parsed subtree promises keyed by final source URL for request deduplication and LRU reuse. */
|
|
10214
|
+
implicitSubtreeCache = /* @__PURE__ */ new Map();
|
|
10215
|
+
/** URLs whose subtree resource requests have not settled. */
|
|
10216
|
+
pendingImplicitSubtreeUrls = /* @__PURE__ */ new Set();
|
|
10217
|
+
/** Maximum number of settled parsed subtrees retained by this source. */
|
|
10218
|
+
maximumCachedSubtrees;
|
|
10219
|
+
/** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
|
|
10220
|
+
implicitTilingStats = {
|
|
10221
|
+
requestedSubtrees: 0,
|
|
10222
|
+
loadedSubtrees: 0,
|
|
10223
|
+
cacheHits: 0,
|
|
10224
|
+
materializedTiles: 0
|
|
10225
|
+
};
|
|
10226
|
+
/** Whether the owning tileset has released this source. */
|
|
10227
|
+
destroyed = false;
|
|
8287
10228
|
extensionsUsed = [];
|
|
8288
10229
|
resolver;
|
|
8289
10230
|
rootTileset;
|
|
@@ -8302,6 +10243,22 @@ var __exports__ = (() => {
|
|
|
8302
10243
|
this.resolver = request.resolver;
|
|
8303
10244
|
this.coreApi = request.coreApi;
|
|
8304
10245
|
this.loadOptions = loadOptions;
|
|
10246
|
+
const maximumCachedSubtrees = Number(
|
|
10247
|
+
loadOptions["3d-tiles"]?.maximumCachedSubtrees
|
|
10248
|
+
);
|
|
10249
|
+
this.maximumCachedSubtrees = Number.isFinite(maximumCachedSubtrees) ? Math.max(0, Math.floor(maximumCachedSubtrees)) : DEFAULT_MAXIMUM_CACHED_SUBTREES;
|
|
10250
|
+
}
|
|
10251
|
+
/**
|
|
10252
|
+
* Releases URL and parsed-subtree caches and blocks late subtree installation.
|
|
10253
|
+
*
|
|
10254
|
+
* The injected resolver or core API remains responsible for transport-level abort signals. A
|
|
10255
|
+
* request that cannot be aborted may finish parsing, but it will not mutate destroyed tiles.
|
|
10256
|
+
*/
|
|
10257
|
+
destroy() {
|
|
10258
|
+
this.destroyed = true;
|
|
10259
|
+
this.tileUrlCache.clear();
|
|
10260
|
+
this.implicitSubtreeCache.clear();
|
|
10261
|
+
this.pendingImplicitSubtreeUrls.clear();
|
|
8305
10262
|
}
|
|
8306
10263
|
/**
|
|
8307
10264
|
* Fetches and validates top-level metadata, then normalizes common fields used by {@link Tileset3D}.
|
|
@@ -8320,7 +10277,9 @@ var __exports__ = (() => {
|
|
|
8320
10277
|
this.tileset = this.rootTileset;
|
|
8321
10278
|
if (this.rootTileset.queryString) {
|
|
8322
10279
|
const searchParams = new URLSearchParams(this.rootTileset.queryString);
|
|
8323
|
-
|
|
10280
|
+
for (const [parameterName, parameterValue] of searchParams.entries()) {
|
|
10281
|
+
this.setQueryParameter(parameterName, parameterValue);
|
|
10282
|
+
}
|
|
8324
10283
|
}
|
|
8325
10284
|
this.asset = this.rootTileset.asset;
|
|
8326
10285
|
if (!this.asset) {
|
|
@@ -8330,7 +10289,7 @@ var __exports__ = (() => {
|
|
|
8330
10289
|
throw new Error("The tileset must be 3D Tiles version either 0.0 or 1.0 or 1.1.");
|
|
8331
10290
|
}
|
|
8332
10291
|
if ("tilesetVersion" in this.asset) {
|
|
8333
|
-
this.
|
|
10292
|
+
this.setQueryParameter("v", this.asset.tilesetVersion);
|
|
8334
10293
|
}
|
|
8335
10294
|
this.properties = this.rootTileset.properties;
|
|
8336
10295
|
this.extras = this.rootTileset.extras;
|
|
@@ -8367,7 +10326,7 @@ var __exports__ = (() => {
|
|
|
8367
10326
|
return this.getMetadata().tileset;
|
|
8368
10327
|
}
|
|
8369
10328
|
/**
|
|
8370
|
-
* Builds
|
|
10329
|
+
* Builds explicit runtime headers while leaving implicit subtree references lazy.
|
|
8371
10330
|
*/
|
|
8372
10331
|
initializeTileHeaders(tileset, tilesetJson, parentTile) {
|
|
8373
10332
|
const rootTile = new Tile3D(tileset, tilesetJson.root, parentTile || void 0);
|
|
@@ -8386,7 +10345,7 @@ var __exports__ = (() => {
|
|
|
8386
10345
|
const url = new URL(childTile.contentUrl);
|
|
8387
10346
|
const session = url.searchParams.get("session");
|
|
8388
10347
|
if (session) {
|
|
8389
|
-
this.
|
|
10348
|
+
this.setQueryParameter("session", session);
|
|
8390
10349
|
}
|
|
8391
10350
|
}
|
|
8392
10351
|
tile.children.push(childTile);
|
|
@@ -8412,7 +10371,9 @@ var __exports__ = (() => {
|
|
|
8412
10371
|
...this.loadOptions,
|
|
8413
10372
|
[this.loader.id]: {
|
|
8414
10373
|
...tilesetLoaderOptions,
|
|
8415
|
-
|
|
10374
|
+
// Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
|
|
10375
|
+
// payloads. This is required for signed and extensionless resources.
|
|
10376
|
+
isTileset: "auto",
|
|
8416
10377
|
assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y"
|
|
8417
10378
|
}
|
|
8418
10379
|
};
|
|
@@ -8420,17 +10381,81 @@ var __exports__ = (() => {
|
|
|
8420
10381
|
tile.content = content;
|
|
8421
10382
|
return {
|
|
8422
10383
|
loaded: true,
|
|
8423
|
-
nestedTileset:
|
|
10384
|
+
nestedTileset: content?.shape === "tileset3d" ? content : void 0
|
|
10385
|
+
};
|
|
10386
|
+
}
|
|
10387
|
+
/**
|
|
10388
|
+
* Loads, materializes, and installs exactly one implicit subtree.
|
|
10389
|
+
*
|
|
10390
|
+
* Final URLs pass through the same query inheritance and archive/custom resolver path as render
|
|
10391
|
+
* content. Parsed resources are deduplicated by final URL, while the pure materializer creates
|
|
10392
|
+
* lazy placeholders for every available child subtree instead of recursively fetching them.
|
|
10393
|
+
*
|
|
10394
|
+
* @param tile - Existing contentless subtree-root placeholder.
|
|
10395
|
+
* @param frameState - View state that made this request eligible; priority is consumed earlier.
|
|
10396
|
+
* @returns Counts describing the installed subtree.
|
|
10397
|
+
*/
|
|
10398
|
+
async loadTileChildren(tile, frameState) {
|
|
10399
|
+
void frameState;
|
|
10400
|
+
const reference = tile.header.implicitSubtree;
|
|
10401
|
+
if (!reference) {
|
|
10402
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
10403
|
+
}
|
|
10404
|
+
const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
|
|
10405
|
+
const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
|
|
10406
|
+
if (this.destroyed || tile.isDestroyed()) {
|
|
10407
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
10408
|
+
}
|
|
10409
|
+
const materializedSubtree = materializeImplicitSubtree(subtree, {
|
|
10410
|
+
...reference,
|
|
10411
|
+
subtreeUrl
|
|
10412
|
+
});
|
|
10413
|
+
tile.applyImplicitSubtreeHeader(materializedSubtree.root);
|
|
10414
|
+
const materializedTileCount = this.initializeMaterializedChildren(
|
|
10415
|
+
tile.tileset,
|
|
10416
|
+
tile,
|
|
10417
|
+
materializedSubtree.root.children
|
|
10418
|
+
);
|
|
10419
|
+
this.implicitTilingStats.loadedSubtrees++;
|
|
10420
|
+
this.implicitTilingStats.materializedTiles += materializedTileCount;
|
|
10421
|
+
return {
|
|
10422
|
+
loaded: true,
|
|
10423
|
+
tileCount: materializedSubtree.tileCount,
|
|
10424
|
+
childSubtreeCount: materializedSubtree.childSubtreeCount
|
|
10425
|
+
};
|
|
10426
|
+
}
|
|
10427
|
+
/**
|
|
10428
|
+
* Returns a snapshot of implicit-subtree request, cache, and materialization counters.
|
|
10429
|
+
*
|
|
10430
|
+
* @returns Immutable-by-convention diagnostic values for runtime inspection.
|
|
10431
|
+
*/
|
|
10432
|
+
getImplicitTilingStats() {
|
|
10433
|
+
return {
|
|
10434
|
+
...this.implicitTilingStats,
|
|
10435
|
+
cachedSubtrees: this.implicitSubtreeCache.size - this.pendingImplicitSubtreeUrls.size,
|
|
10436
|
+
pendingSubtrees: this.pendingImplicitSubtreeUrls.size
|
|
8424
10437
|
};
|
|
8425
10438
|
}
|
|
8426
10439
|
/**
|
|
8427
10440
|
* Resolves a tile content URL with source-managed query parameters.
|
|
10441
|
+
*
|
|
10442
|
+
* Existing per-resource parameters take precedence over inherited root, version, and session
|
|
10443
|
+
* values. Completed URLs are cached by the original tile path; {@link setQueryParameter}
|
|
10444
|
+
* invalidates the cache before changed source state can be observed.
|
|
10445
|
+
*
|
|
10446
|
+
* @param tilePath - Unmodified absolute content URL or data URL from the tile header.
|
|
10447
|
+
* @returns Content URL with any missing source parameters appended.
|
|
8428
10448
|
*/
|
|
8429
10449
|
getTileUrl(tilePath) {
|
|
8430
10450
|
if (tilePath.startsWith("data:")) {
|
|
8431
10451
|
return tilePath;
|
|
8432
10452
|
}
|
|
10453
|
+
const cachedTileUrl = this.tileUrlCache.get(tilePath);
|
|
10454
|
+
if (cachedTileUrl) {
|
|
10455
|
+
return cachedTileUrl;
|
|
10456
|
+
}
|
|
8433
10457
|
if (!Object.keys(this.queryParams).length) {
|
|
10458
|
+
this.tileUrlCache.set(tilePath, tilePath);
|
|
8434
10459
|
return tilePath;
|
|
8435
10460
|
}
|
|
8436
10461
|
const [pathWithoutQuery, existingQuery = ""] = tilePath.split("?");
|
|
@@ -8441,7 +10466,26 @@ var __exports__ = (() => {
|
|
|
8441
10466
|
}
|
|
8442
10467
|
}
|
|
8443
10468
|
const queryParams = mergedQueryParams.toString();
|
|
8444
|
-
|
|
10469
|
+
const tileUrl = queryParams ? `${pathWithoutQuery}?${queryParams}` : pathWithoutQuery;
|
|
10470
|
+
this.tileUrlCache.set(tilePath, tileUrl);
|
|
10471
|
+
return tileUrl;
|
|
10472
|
+
}
|
|
10473
|
+
/**
|
|
10474
|
+
* Updates an inherited source query parameter and invalidates derived request URLs.
|
|
10475
|
+
*
|
|
10476
|
+
* Root tokens and tileset versions normally settle during initialization. Some providers expose
|
|
10477
|
+
* a session parameter on a child URL, so invalidation is required to prevent URLs cached earlier
|
|
10478
|
+
* in header construction from retaining stale authentication state.
|
|
10479
|
+
*
|
|
10480
|
+
* @param parameterName - Query parameter name.
|
|
10481
|
+
* @param parameterValue - Query parameter value.
|
|
10482
|
+
*/
|
|
10483
|
+
setQueryParameter(parameterName, parameterValue) {
|
|
10484
|
+
if (this.queryParams[parameterName] === parameterValue) {
|
|
10485
|
+
return;
|
|
10486
|
+
}
|
|
10487
|
+
this.queryParams[parameterName] = parameterValue;
|
|
10488
|
+
this.tileUrlCache.clear();
|
|
8445
10489
|
}
|
|
8446
10490
|
/**
|
|
8447
10491
|
* Derives the default view state from the root bounding volume.
|
|
@@ -8501,11 +10545,11 @@ var __exports__ = (() => {
|
|
|
8501
10545
|
/**
|
|
8502
10546
|
* Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
|
|
8503
10547
|
*/
|
|
8504
|
-
async loadWithCoreApi(url, options) {
|
|
10548
|
+
async loadWithCoreApi(url, options, loader = this.loader) {
|
|
8505
10549
|
if (!this.coreApi) {
|
|
8506
10550
|
throw new Error("Tiles3DSource requires an injected coreApi to load tileset data");
|
|
8507
10551
|
}
|
|
8508
|
-
return await this.coreApi.load(url,
|
|
10552
|
+
return await this.coreApi.load(url, loader, options);
|
|
8509
10553
|
}
|
|
8510
10554
|
/**
|
|
8511
10555
|
* Loads data through an injected resolver when present, otherwise through the injected core API.
|
|
@@ -8517,13 +10561,91 @@ var __exports__ = (() => {
|
|
|
8517
10561
|
return await this.loadWithCoreApi(url, options);
|
|
8518
10562
|
}
|
|
8519
10563
|
/**
|
|
8520
|
-
* Loads
|
|
10564
|
+
* Loads an arbitrary source resource through an injected resolver or core API.
|
|
8521
10565
|
*/
|
|
8522
|
-
async loadResourceData(url, options) {
|
|
10566
|
+
async loadResourceData(url, options, loader = this.loader) {
|
|
8523
10567
|
if (this.resolver) {
|
|
8524
|
-
return await this.resolver.loadResource(url,
|
|
10568
|
+
return await this.resolver.loadResource(url, loader, options);
|
|
10569
|
+
}
|
|
10570
|
+
return await this.loadWithCoreApi(url, options, loader);
|
|
10571
|
+
}
|
|
10572
|
+
/**
|
|
10573
|
+
* Returns a parsed subtree from the LRU cache or starts one source-managed request.
|
|
10574
|
+
*
|
|
10575
|
+
* @param subtreeUrl - Final subtree URL after query inheritance.
|
|
10576
|
+
* @returns Parsed subtree availability data.
|
|
10577
|
+
*/
|
|
10578
|
+
async loadImplicitSubtreeResource(subtreeUrl) {
|
|
10579
|
+
const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
|
|
10580
|
+
if (cachedSubtree) {
|
|
10581
|
+
this.implicitTilingStats.cacheHits++;
|
|
10582
|
+
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
10583
|
+
this.implicitSubtreeCache.set(subtreeUrl, cachedSubtree);
|
|
10584
|
+
return await cachedSubtree;
|
|
10585
|
+
}
|
|
10586
|
+
this.implicitTilingStats.requestedSubtrees++;
|
|
10587
|
+
this.pendingImplicitSubtreeUrls.add(subtreeUrl);
|
|
10588
|
+
const loaderOptions = this.loadOptions[this.loader.id] || {};
|
|
10589
|
+
const subtreePromise = this.loadResourceData(subtreeUrl, {
|
|
10590
|
+
...this.loadOptions,
|
|
10591
|
+
[this.loader.id]: {
|
|
10592
|
+
...loaderOptions,
|
|
10593
|
+
isTileset: false,
|
|
10594
|
+
isSubtree: true
|
|
10595
|
+
}
|
|
10596
|
+
});
|
|
10597
|
+
this.implicitSubtreeCache.set(subtreeUrl, subtreePromise);
|
|
10598
|
+
try {
|
|
10599
|
+
return await subtreePromise;
|
|
10600
|
+
} catch (error) {
|
|
10601
|
+
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
10602
|
+
throw error;
|
|
10603
|
+
} finally {
|
|
10604
|
+
this.pendingImplicitSubtreeUrls.delete(subtreeUrl);
|
|
10605
|
+
this.trimImplicitSubtreeCache();
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
/**
|
|
10609
|
+
* Instantiates every header represented by one materialized subtree.
|
|
10610
|
+
*
|
|
10611
|
+
* @param tileset - Owning runtime tileset.
|
|
10612
|
+
* @param parentTile - Existing materialized parent.
|
|
10613
|
+
* @param childHeaders - Headers to install below the parent.
|
|
10614
|
+
* @returns Number of newly allocated runtime tile nodes.
|
|
10615
|
+
*/
|
|
10616
|
+
initializeMaterializedChildren(tileset, parentTile, childHeaders) {
|
|
10617
|
+
let materializedTileCount = 0;
|
|
10618
|
+
const stack = [
|
|
10619
|
+
{ parentTile, childHeaders }
|
|
10620
|
+
];
|
|
10621
|
+
while (stack.length > 0) {
|
|
10622
|
+
const entry = stack.pop();
|
|
10623
|
+
for (const childHeader of entry.childHeaders) {
|
|
10624
|
+
const childTile = new Tile3D(tileset, childHeader, entry.parentTile);
|
|
10625
|
+
entry.parentTile.children.push(childTile);
|
|
10626
|
+
childTile.depth = entry.parentTile.depth + 1;
|
|
10627
|
+
tileset.stats.get("Tiles In Tileset(s)").incrementCount();
|
|
10628
|
+
materializedTileCount++;
|
|
10629
|
+
if (childHeader.children?.length) {
|
|
10630
|
+
stack.push({ parentTile: childTile, childHeaders: childHeader.children });
|
|
10631
|
+
}
|
|
10632
|
+
}
|
|
10633
|
+
}
|
|
10634
|
+
return materializedTileCount;
|
|
10635
|
+
}
|
|
10636
|
+
/** Evicts least-recently-used settled subtree entries until the configured bound is met. */
|
|
10637
|
+
trimImplicitSubtreeCache() {
|
|
10638
|
+
if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
|
|
10639
|
+
return;
|
|
10640
|
+
}
|
|
10641
|
+
for (const subtreeUrl of this.implicitSubtreeCache.keys()) {
|
|
10642
|
+
if (!this.pendingImplicitSubtreeUrls.has(subtreeUrl)) {
|
|
10643
|
+
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
10644
|
+
}
|
|
10645
|
+
if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
|
|
10646
|
+
break;
|
|
10647
|
+
}
|
|
8525
10648
|
}
|
|
8526
|
-
return await this.loadWithCoreApi(url, options);
|
|
8527
10649
|
}
|
|
8528
10650
|
};
|
|
8529
10651
|
function isTilesetRequest(input) {
|
|
@@ -8738,7 +10860,7 @@ var __exports__ = (() => {
|
|
|
8738
10860
|
tile.children.push(childTile);
|
|
8739
10861
|
const frameState = this._tileManager.find(childTile.id).frameState;
|
|
8740
10862
|
this.updateTile(childTile, frameState);
|
|
8741
|
-
if (this._frameNumber === frameState.frameNumber && (this.traversalFinished(frameState) || new Date().getTime() - this.lastUpdate > this.updateDebounceTime)) {
|
|
10863
|
+
if (this._frameNumber === frameState.frameNumber && (this.traversalFinished(frameState) || (/* @__PURE__ */ new Date()).getTime() - this.lastUpdate > this.updateDebounceTime)) {
|
|
8742
10864
|
this.executeTraversal(childTile, frameState);
|
|
8743
10865
|
}
|
|
8744
10866
|
}
|
|
@@ -9234,7 +11356,7 @@ var __exports__ = (() => {
|
|
|
9234
11356
|
},
|
|
9235
11357
|
tileSource: null
|
|
9236
11358
|
};
|
|
9237
|
-
var Tileset2D = class {
|
|
11359
|
+
var Tileset2D = class _Tileset2D {
|
|
9238
11360
|
/** Live counters describing shared tileset state. */
|
|
9239
11361
|
stats;
|
|
9240
11362
|
/** Cached metadata returned by the backing TileSource, if any. */
|
|
@@ -9296,7 +11418,7 @@ var __exports__ = (() => {
|
|
|
9296
11418
|
}
|
|
9297
11419
|
/** Convenience factory for wrapping a loaders.gl `TileSource`. */
|
|
9298
11420
|
static fromTileSource(tileSource, opts = {}) {
|
|
9299
|
-
return new
|
|
11421
|
+
return new _Tileset2D({ ...opts, tileSource });
|
|
9300
11422
|
}
|
|
9301
11423
|
/** All tiles currently present in the shared cache. */
|
|
9302
11424
|
get tiles() {
|
|
@@ -9738,7 +11860,7 @@ var __exports__ = (() => {
|
|
|
9738
11860
|
getImage: async () => null,
|
|
9739
11861
|
debounceTime: 0
|
|
9740
11862
|
};
|
|
9741
|
-
var ImageSet = class {
|
|
11863
|
+
var ImageSet = class _ImageSet {
|
|
9742
11864
|
/** Cached metadata returned by the backing source, if any. */
|
|
9743
11865
|
metadata = null;
|
|
9744
11866
|
_opts;
|
|
@@ -9761,7 +11883,7 @@ var __exports__ = (() => {
|
|
|
9761
11883
|
}
|
|
9762
11884
|
/** Convenience factory for wrapping a loaders.gl {@link ImageSource}. */
|
|
9763
11885
|
static fromImageSource(imageSource, opts = {}) {
|
|
9764
|
-
return new
|
|
11886
|
+
return new _ImageSet({ ...opts, imageSource });
|
|
9765
11887
|
}
|
|
9766
11888
|
/** Whether the manager has no requests in flight and a current image. */
|
|
9767
11889
|
get isLoaded() {
|
|
@@ -9870,7 +11992,7 @@ var __exports__ = (() => {
|
|
|
9870
11992
|
}
|
|
9871
11993
|
this._emitUpdate();
|
|
9872
11994
|
} catch (error) {
|
|
9873
|
-
if (
|
|
11995
|
+
if (isAbortError2(error)) {
|
|
9874
11996
|
return;
|
|
9875
11997
|
}
|
|
9876
11998
|
const normalizedError = normalizeImageSetError(error, "Image request failed");
|
|
@@ -9951,7 +12073,7 @@ var __exports__ = (() => {
|
|
|
9951
12073
|
this._abortController = null;
|
|
9952
12074
|
}
|
|
9953
12075
|
};
|
|
9954
|
-
function
|
|
12076
|
+
function isAbortError2(error) {
|
|
9955
12077
|
return error instanceof Error && error.name === "AbortError";
|
|
9956
12078
|
}
|
|
9957
12079
|
function normalizeImageSetError(error, message) {
|
|
@@ -9982,7 +12104,7 @@ var __exports__ = (() => {
|
|
|
9982
12104
|
debounceTime: 0,
|
|
9983
12105
|
shouldRefetch: () => true
|
|
9984
12106
|
};
|
|
9985
|
-
var RasterSet = class {
|
|
12107
|
+
var RasterSet = class _RasterSet {
|
|
9986
12108
|
/** Cached metadata returned by the backing source, if any. */
|
|
9987
12109
|
metadata = null;
|
|
9988
12110
|
_opts;
|
|
@@ -10005,7 +12127,7 @@ var __exports__ = (() => {
|
|
|
10005
12127
|
}
|
|
10006
12128
|
/** Convenience factory for wrapping a loaders.gl {@link RasterSource}. */
|
|
10007
12129
|
static fromRasterSource(rasterSource, opts = {}) {
|
|
10008
|
-
return new
|
|
12130
|
+
return new _RasterSet({ ...opts, rasterSource });
|
|
10009
12131
|
}
|
|
10010
12132
|
/** Whether the manager has no requests in flight and a current raster. */
|
|
10011
12133
|
get isLoaded() {
|
|
@@ -10124,7 +12246,7 @@ var __exports__ = (() => {
|
|
|
10124
12246
|
}
|
|
10125
12247
|
this._emitUpdate();
|
|
10126
12248
|
} catch (error) {
|
|
10127
|
-
if (
|
|
12249
|
+
if (isAbortError3(error)) {
|
|
10128
12250
|
return;
|
|
10129
12251
|
}
|
|
10130
12252
|
const normalizedError = normalizeRasterSetError(error, "Raster request failed");
|
|
@@ -10213,7 +12335,7 @@ var __exports__ = (() => {
|
|
|
10213
12335
|
}
|
|
10214
12336
|
return new Error(message);
|
|
10215
12337
|
}
|
|
10216
|
-
function
|
|
12338
|
+
function isAbortError3(error) {
|
|
10217
12339
|
return error instanceof Error && error.name === "AbortError";
|
|
10218
12340
|
}
|
|
10219
12341
|
|
|
@@ -10283,7 +12405,7 @@ var __exports__ = (() => {
|
|
|
10283
12405
|
};
|
|
10284
12406
|
|
|
10285
12407
|
// src/point-cloud/point-cloud-tileset.ts
|
|
10286
|
-
var
|
|
12408
|
+
var DEFAULT_PROPS5 = {
|
|
10287
12409
|
debounceTime: 0,
|
|
10288
12410
|
minimumNodePixelSize: 150,
|
|
10289
12411
|
maximumScreenSpaceError: 1,
|
|
@@ -10313,7 +12435,7 @@ var __exports__ = (() => {
|
|
|
10313
12435
|
tilesMap = /* @__PURE__ */ new Map();
|
|
10314
12436
|
constructor(dataSource, options = {}) {
|
|
10315
12437
|
this.dataSource = dataSource;
|
|
10316
|
-
this.options = { ...
|
|
12438
|
+
this.options = { ...DEFAULT_PROPS5, ...options };
|
|
10317
12439
|
this.tilesetInitializationPromise = this.initialize();
|
|
10318
12440
|
}
|
|
10319
12441
|
tilesetInitializationPromise;
|