@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/index.cjs
CHANGED
|
@@ -18,8 +18,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
20
|
// dist/index.js
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
23
|
I3SSource: () => I3SSource,
|
|
24
24
|
ImageSet: () => ImageSet,
|
|
25
25
|
IndexedArchiveTilesetSource: () => IndexedArchiveTilesetSource,
|
|
@@ -43,17 +43,1362 @@ __export(dist_exports, {
|
|
|
43
43
|
TilesetTraverser: () => TilesetTraverser,
|
|
44
44
|
calculateTransformProps: () => calculateTransformProps,
|
|
45
45
|
createBoundingVolume: () => createBoundingVolume,
|
|
46
|
+
createImplicitSubtreeReference: () => createImplicitSubtreeReference,
|
|
46
47
|
getFrameState: () => getFrameState,
|
|
47
48
|
getLodStatus: () => getLodStatus,
|
|
48
|
-
isTileset3DSource: () => isTileset3DSource
|
|
49
|
+
isTileset3DSource: () => isTileset3DSource,
|
|
50
|
+
materializeImplicitSubtree: () => materializeImplicitSubtree,
|
|
51
|
+
replaceImplicitUrlTemplate: () => replaceImplicitUrlTemplate
|
|
49
52
|
});
|
|
50
|
-
module.exports = __toCommonJS(
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
51
54
|
|
|
52
55
|
// dist/tileset-3d/common/tileset-3d.js
|
|
53
|
-
var
|
|
56
|
+
var import_core5 = require("@math.gl/core");
|
|
54
57
|
var import_geospatial3 = require("@math.gl/geospatial");
|
|
58
|
+
var import_stats3 = require("@probe.gl/stats");
|
|
59
|
+
|
|
60
|
+
// ../loader-utils/src/lib/env-utils/assert.ts
|
|
61
|
+
function assert(condition, message) {
|
|
62
|
+
if (!condition) {
|
|
63
|
+
throw new Error(message || "loader assertion failed.");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ../loader-utils/src/lib/request-utils/request-scheduler.ts
|
|
55
68
|
var import_stats = require("@probe.gl/stats");
|
|
56
|
-
var
|
|
69
|
+
var STAT_QUEUED_REQUESTS = "Queued Requests";
|
|
70
|
+
var STAT_ACTIVE_REQUESTS = "Active Requests";
|
|
71
|
+
var STAT_CANCELLED_REQUESTS = "Cancelled Requests";
|
|
72
|
+
var STAT_QUEUED_REQUESTS_EVER = "Queued Requests Ever";
|
|
73
|
+
var STAT_ACTIVE_REQUESTS_EVER = "Active Requests Ever";
|
|
74
|
+
var DEFAULT_PROPS = {
|
|
75
|
+
id: "request-scheduler",
|
|
76
|
+
/** Specifies if the request scheduler should throttle incoming requests, mainly for comparative testing. */
|
|
77
|
+
throttleRequests: true,
|
|
78
|
+
/** The maximum number of simultaneous active requests. Un-throttled requests do not observe this limit. */
|
|
79
|
+
maxRequests: 6,
|
|
80
|
+
/**
|
|
81
|
+
* Specifies a debounce time, in milliseconds. All requests are queued, until no new requests have
|
|
82
|
+
* been added to the queue for this amount of time.
|
|
83
|
+
*/
|
|
84
|
+
debounceTime: 0
|
|
85
|
+
};
|
|
86
|
+
var RequestScheduler = class {
|
|
87
|
+
props;
|
|
88
|
+
stats;
|
|
89
|
+
activeRequestCount = 0;
|
|
90
|
+
/** Tracks the number of active requests and prioritizes/cancels queued requests. */
|
|
91
|
+
requestQueue = [];
|
|
92
|
+
requestMap = /* @__PURE__ */ new Map();
|
|
93
|
+
updateTimer = null;
|
|
94
|
+
constructor(props = {}) {
|
|
95
|
+
this.props = { ...DEFAULT_PROPS, ...props };
|
|
96
|
+
this.stats = new import_stats.Stats({ id: this.props.id });
|
|
97
|
+
this.stats.get(STAT_QUEUED_REQUESTS);
|
|
98
|
+
this.stats.get(STAT_ACTIVE_REQUESTS);
|
|
99
|
+
this.stats.get(STAT_CANCELLED_REQUESTS);
|
|
100
|
+
this.stats.get(STAT_QUEUED_REQUESTS_EVER);
|
|
101
|
+
this.stats.get(STAT_ACTIVE_REQUESTS_EVER);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Update scheduler props. Preserves active and queued requests.
|
|
105
|
+
*/
|
|
106
|
+
setProps(props) {
|
|
107
|
+
if (props.throttleRequests !== void 0) {
|
|
108
|
+
this.props.throttleRequests = props.throttleRequests;
|
|
109
|
+
}
|
|
110
|
+
if (props.maxRequests !== void 0) {
|
|
111
|
+
this.props.maxRequests = props.maxRequests;
|
|
112
|
+
}
|
|
113
|
+
if (props.debounceTime !== void 0) {
|
|
114
|
+
this.props.debounceTime = props.debounceTime;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Called by an application that wants to issue a request, without having it deeply queued by the browser
|
|
119
|
+
*
|
|
120
|
+
* When the returned promise resolved, it is OK for the application to issue a request.
|
|
121
|
+
* The promise resolves to an object that contains a `done` method.
|
|
122
|
+
* When the application's request has completed (or failed), the application must call the `done` function
|
|
123
|
+
*
|
|
124
|
+
* @param handle
|
|
125
|
+
* @param getPriority will be called when request "slots" open up,
|
|
126
|
+
* allowing the caller to update priority or cancel the request
|
|
127
|
+
* Highest priority executes first, priority < 0 cancels the request
|
|
128
|
+
* @returns a promise
|
|
129
|
+
* - resolves to a object (with a `done` field) when the request can be issued without queueing,
|
|
130
|
+
* - resolves to `null` if the request has been cancelled (by the callback return < 0).
|
|
131
|
+
* In this case the application should not issue the request
|
|
132
|
+
*/
|
|
133
|
+
scheduleRequest(handle, getPriority = () => 0) {
|
|
134
|
+
if (!this.props.throttleRequests) {
|
|
135
|
+
return Promise.resolve({ done: () => {
|
|
136
|
+
} });
|
|
137
|
+
}
|
|
138
|
+
if (this.requestMap.has(handle)) {
|
|
139
|
+
return this.requestMap.get(handle);
|
|
140
|
+
}
|
|
141
|
+
const request = { handle, priority: 0, getPriority };
|
|
142
|
+
const promise = new Promise((resolve2) => {
|
|
143
|
+
request.resolve = resolve2;
|
|
144
|
+
return request;
|
|
145
|
+
});
|
|
146
|
+
this.requestQueue.push(request);
|
|
147
|
+
this.requestMap.set(handle, promise);
|
|
148
|
+
this._issueNewRequests();
|
|
149
|
+
return promise;
|
|
150
|
+
}
|
|
151
|
+
// PRIVATE
|
|
152
|
+
_issueRequest(request) {
|
|
153
|
+
const { handle, resolve: resolve2 } = request;
|
|
154
|
+
let isDone = false;
|
|
155
|
+
const done = () => {
|
|
156
|
+
if (!isDone) {
|
|
157
|
+
isDone = true;
|
|
158
|
+
this.requestMap.delete(handle);
|
|
159
|
+
this.activeRequestCount--;
|
|
160
|
+
this._issueNewRequests();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
this.activeRequestCount++;
|
|
164
|
+
return resolve2 ? resolve2({ done }) : Promise.resolve({ done });
|
|
165
|
+
}
|
|
166
|
+
/** We check requests asynchronously, to prevent multiple updates */
|
|
167
|
+
_issueNewRequests() {
|
|
168
|
+
if (this.updateTimer !== null) {
|
|
169
|
+
clearTimeout(this.updateTimer);
|
|
170
|
+
}
|
|
171
|
+
this.updateTimer = setTimeout(() => this._issueNewRequestsAsync(), this.props.debounceTime);
|
|
172
|
+
}
|
|
173
|
+
/** Refresh all requests */
|
|
174
|
+
_issueNewRequestsAsync() {
|
|
175
|
+
if (this.updateTimer !== null) {
|
|
176
|
+
clearTimeout(this.updateTimer);
|
|
177
|
+
}
|
|
178
|
+
this.updateTimer = null;
|
|
179
|
+
const freeSlots = Math.max(this.props.maxRequests - this.activeRequestCount, 0);
|
|
180
|
+
if (freeSlots === 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
this._updateAllRequests();
|
|
184
|
+
for (let i = 0; i < freeSlots; ++i) {
|
|
185
|
+
const request = this.requestQueue.shift();
|
|
186
|
+
if (request) {
|
|
187
|
+
this._issueRequest(request);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** Ensure all requests have updated priorities, and that no longer valid requests are cancelled */
|
|
192
|
+
_updateAllRequests() {
|
|
193
|
+
const requestQueue = this.requestQueue;
|
|
194
|
+
for (let i = 0; i < requestQueue.length; ++i) {
|
|
195
|
+
const request = requestQueue[i];
|
|
196
|
+
if (!this._updateRequest(request)) {
|
|
197
|
+
requestQueue.splice(i, 1);
|
|
198
|
+
this.requestMap.delete(request.handle);
|
|
199
|
+
i--;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
requestQueue.sort((a, b) => a.priority - b.priority);
|
|
203
|
+
}
|
|
204
|
+
/** Update a single request by calling the callback */
|
|
205
|
+
_updateRequest(request) {
|
|
206
|
+
request.priority = request.getPriority(request.handle);
|
|
207
|
+
if (request.priority < 0) {
|
|
208
|
+
request.resolve(null);
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// ../loader-utils/src/lib/request-utils/range-request-scheduler.ts
|
|
216
|
+
var import_stats2 = require("@probe.gl/stats");
|
|
217
|
+
var RANGE_STATS_KEYS = {
|
|
218
|
+
logicalRanges: "Logical Range Requests",
|
|
219
|
+
rangeBatches: "Range Request Batches",
|
|
220
|
+
transportRanges: "Transport Ranges Created",
|
|
221
|
+
completedTransportRanges: "Range Transport Requests Completed",
|
|
222
|
+
coalescedRanges: "Coalesced Logical Ranges",
|
|
223
|
+
requestedBytes: "Logical Range Request Bytes",
|
|
224
|
+
transportBytes: "Range Transport Bytes Requested",
|
|
225
|
+
responseBytes: "Range Response Bytes",
|
|
226
|
+
networkTimeMs: "Range Network Time",
|
|
227
|
+
overfetchBytes: "Range Overfetch Bytes",
|
|
228
|
+
failedTransportRanges: "Range Transport Requests Failed",
|
|
229
|
+
abortedLogicalRanges: "Aborted Logical Range Requests",
|
|
230
|
+
fullResponseFallbacks: "Full Response Fallbacks"
|
|
231
|
+
};
|
|
232
|
+
var DEFAULT_PROPS2 = {
|
|
233
|
+
batchDelayMs: 50,
|
|
234
|
+
maxGapBytes: 65536,
|
|
235
|
+
rangeExpansionBytes: 65536,
|
|
236
|
+
maxMergedBytes: 8388608,
|
|
237
|
+
stats: createRangeStats("range-request-scheduler-default"),
|
|
238
|
+
onEvent: () => {
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var RangeRequestScheduler = class {
|
|
242
|
+
/** Runtime scheduler configuration with defaults filled in. */
|
|
243
|
+
props;
|
|
244
|
+
/** Runtime counters for range batching and transport behavior. */
|
|
245
|
+
stats;
|
|
246
|
+
/** Optional event callback for range batching diagnostics. */
|
|
247
|
+
onEvent;
|
|
248
|
+
pendingRequests = [];
|
|
249
|
+
flushTimer = null;
|
|
250
|
+
/** Creates a scheduler for one group of byte-range-addressable resources. */
|
|
251
|
+
constructor(props = {}) {
|
|
252
|
+
const rangeExpansionBytes = props.rangeExpansionBytes ?? props.maxGapBytes ?? DEFAULT_PROPS2.rangeExpansionBytes;
|
|
253
|
+
this.props = {
|
|
254
|
+
batchDelayMs: props.batchDelayMs ?? DEFAULT_PROPS2.batchDelayMs,
|
|
255
|
+
maxGapBytes: props.maxGapBytes ?? rangeExpansionBytes,
|
|
256
|
+
rangeExpansionBytes,
|
|
257
|
+
maxMergedBytes: props.maxMergedBytes ?? DEFAULT_PROPS2.maxMergedBytes
|
|
258
|
+
};
|
|
259
|
+
this.stats = props.stats || createRangeStats("range-request-scheduler");
|
|
260
|
+
this.onEvent = props.onEvent;
|
|
261
|
+
initializeStats(this.stats);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Enqueues one byte range request and resolves with the exact requested byte slice.
|
|
265
|
+
*/
|
|
266
|
+
scheduleRequest(request) {
|
|
267
|
+
var _a;
|
|
268
|
+
if (request.length < 0) {
|
|
269
|
+
return Promise.reject(new Error("Byte range length cannot be negative"));
|
|
270
|
+
}
|
|
271
|
+
if ((_a = request.signal) == null ? void 0 : _a.aborted) {
|
|
272
|
+
return Promise.reject(createAbortError());
|
|
273
|
+
}
|
|
274
|
+
if (request.length === 0) {
|
|
275
|
+
return Promise.resolve(new ArrayBuffer(0));
|
|
276
|
+
}
|
|
277
|
+
const promise = new Promise((resolve2, reject) => {
|
|
278
|
+
const pendingRequest = { ...request, resolve: resolve2, reject, settled: false };
|
|
279
|
+
if (request.signal) {
|
|
280
|
+
pendingRequest.abortListener = () => {
|
|
281
|
+
var _a2;
|
|
282
|
+
this.abortRequest(pendingRequest);
|
|
283
|
+
(_a2 = pendingRequest.onAbort) == null ? void 0 : _a2.call(pendingRequest);
|
|
284
|
+
};
|
|
285
|
+
request.signal.addEventListener("abort", pendingRequest.abortListener, { once: true });
|
|
286
|
+
}
|
|
287
|
+
this.pendingRequests.push(pendingRequest);
|
|
288
|
+
});
|
|
289
|
+
this.trackEvent({
|
|
290
|
+
type: "queued",
|
|
291
|
+
sourceId: request.sourceId,
|
|
292
|
+
offset: request.offset,
|
|
293
|
+
length: request.length,
|
|
294
|
+
logicalRequestCount: 1,
|
|
295
|
+
logicalBytes: request.length
|
|
296
|
+
});
|
|
297
|
+
this.scheduleFlush();
|
|
298
|
+
return promise;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Enqueues one HTTP byte-range request and resolves with the exact requested byte slice.
|
|
302
|
+
*/
|
|
303
|
+
fetch(request) {
|
|
304
|
+
const fetchFunction = request.fetch || globalThis.fetch;
|
|
305
|
+
return this.scheduleRequest({
|
|
306
|
+
sourceId: request.sourceId || request.url,
|
|
307
|
+
offset: request.offset,
|
|
308
|
+
length: request.length,
|
|
309
|
+
signal: request.signal,
|
|
310
|
+
// Different fetch calls may carry different credentials or transport implementations.
|
|
311
|
+
// Sharing is therefore opt-in through an explicit common isolation key.
|
|
312
|
+
isolationKey: request.isolationKey ?? {},
|
|
313
|
+
fetchRange: (offset, length, signal) => fetchHttpRange({
|
|
314
|
+
url: request.url,
|
|
315
|
+
offset,
|
|
316
|
+
length,
|
|
317
|
+
signal,
|
|
318
|
+
fetch: fetchFunction,
|
|
319
|
+
fetchOptions: request.fetchOptions
|
|
320
|
+
})
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/** Immediately starts loading the currently queued requests. */
|
|
324
|
+
flush() {
|
|
325
|
+
if (this.flushTimer) {
|
|
326
|
+
clearTimeout(this.flushTimer);
|
|
327
|
+
this.flushTimer = null;
|
|
328
|
+
}
|
|
329
|
+
const pendingRequests = this.pendingRequests;
|
|
330
|
+
this.pendingRequests = [];
|
|
331
|
+
const activeRequests = pendingRequests.filter((request) => {
|
|
332
|
+
var _a;
|
|
333
|
+
if (request.settled || ((_a = request.signal) == null ? void 0 : _a.aborted)) {
|
|
334
|
+
this.abortRequest(request);
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
});
|
|
339
|
+
const mergedRequests = this.mergeRequests(activeRequests);
|
|
340
|
+
if (activeRequests.length > 0) {
|
|
341
|
+
this.trackBatch(activeRequests, mergedRequests);
|
|
342
|
+
}
|
|
343
|
+
for (const mergedRequest of mergedRequests) {
|
|
344
|
+
this.fetchMergedRequest(mergedRequest);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** Schedules the next pending-request flush. */
|
|
348
|
+
scheduleFlush() {
|
|
349
|
+
if (this.flushTimer) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
this.flushTimer = setTimeout(() => this.flush(), this.props.batchDelayMs);
|
|
353
|
+
}
|
|
354
|
+
/** Sorts active requests and combines requests for the same source when thresholds allow. */
|
|
355
|
+
mergeRequests(requests) {
|
|
356
|
+
requests.sort((left, right) => {
|
|
357
|
+
if (left.sourceId !== right.sourceId) {
|
|
358
|
+
return left.sourceId < right.sourceId ? -1 : 1;
|
|
359
|
+
}
|
|
360
|
+
return left.offset - right.offset;
|
|
361
|
+
});
|
|
362
|
+
const mergedRequests = [];
|
|
363
|
+
for (const request of requests) {
|
|
364
|
+
const requestEndOffset = request.offset + request.length;
|
|
365
|
+
const lastMergedRequest = mergedRequests[mergedRequests.length - 1];
|
|
366
|
+
if (lastMergedRequest && canMergeRequests(lastMergedRequest, request, this.props)) {
|
|
367
|
+
lastMergedRequest.endOffset = Math.max(lastMergedRequest.endOffset, requestEndOffset);
|
|
368
|
+
lastMergedRequest.requests.push(request);
|
|
369
|
+
} else {
|
|
370
|
+
mergedRequests.push({
|
|
371
|
+
sourceId: request.sourceId,
|
|
372
|
+
isolationKey: request.isolationKey ?? request.sourceId,
|
|
373
|
+
offset: request.offset,
|
|
374
|
+
endOffset: requestEndOffset,
|
|
375
|
+
fetchRange: request.fetchRange,
|
|
376
|
+
requests: [request]
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return mergedRequests;
|
|
381
|
+
}
|
|
382
|
+
/** Loads one merged range and resolves each child request with its sliced bytes. */
|
|
383
|
+
async fetchMergedRequest(mergedRequest) {
|
|
384
|
+
var _a, _b;
|
|
385
|
+
const { offset, endOffset, fetchRange, requests } = mergedRequest;
|
|
386
|
+
const abortController = new AbortController();
|
|
387
|
+
const requestStartTime = getTimestamp();
|
|
388
|
+
const abortTransportIfUnused = () => {
|
|
389
|
+
if (requests.every((request) => {
|
|
390
|
+
var _a2;
|
|
391
|
+
return (_a2 = request.signal) == null ? void 0 : _a2.aborted;
|
|
392
|
+
})) {
|
|
393
|
+
abortController.abort();
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
try {
|
|
397
|
+
for (const request of requests) {
|
|
398
|
+
request.onAbort = abortTransportIfUnused;
|
|
399
|
+
}
|
|
400
|
+
abortTransportIfUnused();
|
|
401
|
+
if (requests.every((request) => request.settled)) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const logicalBytes = getLogicalRequestBytes(requests);
|
|
405
|
+
const length = endOffset - offset;
|
|
406
|
+
this.trackEvent({
|
|
407
|
+
type: "request",
|
|
408
|
+
sourceId: mergedRequest.sourceId,
|
|
409
|
+
offset,
|
|
410
|
+
length,
|
|
411
|
+
logicalRequestCount: requests.length,
|
|
412
|
+
logicalBytes,
|
|
413
|
+
transportBytes: length,
|
|
414
|
+
overfetchBytes: Math.max(length - logicalBytes, 0)
|
|
415
|
+
});
|
|
416
|
+
const transportResult = normalizeTransportResult(
|
|
417
|
+
await fetchRange(offset, length, abortController.signal)
|
|
418
|
+
);
|
|
419
|
+
const { arrayBuffer } = transportResult;
|
|
420
|
+
const isClampedFullResponse = transportResult.fullResponse && offset === 0 && arrayBuffer.byteLength < length;
|
|
421
|
+
const isClampedEndOfFile = arrayBuffer.byteLength < length && Number.isSafeInteger(transportResult.sourceByteLength) && offset + arrayBuffer.byteLength === transportResult.sourceByteLength;
|
|
422
|
+
if (arrayBuffer.byteLength !== length && !isClampedFullResponse && !isClampedEndOfFile) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`Byte-range transport returned ${arrayBuffer.byteLength} bytes; expected ${length}`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const networkTimeMs = transportResult.networkTimeMs ?? getTimestamp() - requestStartTime;
|
|
428
|
+
this.trackEvent({
|
|
429
|
+
type: "response",
|
|
430
|
+
sourceId: mergedRequest.sourceId,
|
|
431
|
+
offset,
|
|
432
|
+
length,
|
|
433
|
+
logicalRequestCount: requests.length,
|
|
434
|
+
logicalBytes,
|
|
435
|
+
transportBytes: length,
|
|
436
|
+
responseBytes: transportResult.transportBytes ?? arrayBuffer.byteLength,
|
|
437
|
+
networkTimeMs,
|
|
438
|
+
overfetchBytes: Math.max(length - logicalBytes, 0),
|
|
439
|
+
status: transportResult.status,
|
|
440
|
+
fullResponse: transportResult.fullResponse
|
|
441
|
+
});
|
|
442
|
+
for (const request of requests) {
|
|
443
|
+
if (request.settled) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const start = request.offset - offset;
|
|
447
|
+
request.settled = true;
|
|
448
|
+
request.resolve(arrayBuffer.slice(start, start + request.length));
|
|
449
|
+
}
|
|
450
|
+
} catch (error) {
|
|
451
|
+
this.trackEvent({
|
|
452
|
+
type: "error",
|
|
453
|
+
sourceId: mergedRequest.sourceId,
|
|
454
|
+
offset,
|
|
455
|
+
length: endOffset - offset,
|
|
456
|
+
logicalRequestCount: requests.length,
|
|
457
|
+
logicalBytes: getLogicalRequestBytes(requests),
|
|
458
|
+
networkTimeMs: getTimestamp() - requestStartTime,
|
|
459
|
+
error
|
|
460
|
+
});
|
|
461
|
+
for (const request of requests) {
|
|
462
|
+
if (request.settled) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if ((_a = request.signal) == null ? void 0 : _a.aborted) {
|
|
466
|
+
this.abortRequest(request);
|
|
467
|
+
} else {
|
|
468
|
+
request.settled = true;
|
|
469
|
+
request.reject(error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
} finally {
|
|
473
|
+
for (const request of requests) {
|
|
474
|
+
if (request.abortListener) {
|
|
475
|
+
(_b = request.signal) == null ? void 0 : _b.removeEventListener("abort", request.abortListener);
|
|
476
|
+
}
|
|
477
|
+
request.onAbort = void 0;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/** Rejects and tracks one logical request exactly once when its caller aborts. */
|
|
482
|
+
abortRequest(request) {
|
|
483
|
+
if (request.settled) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
request.settled = true;
|
|
487
|
+
request.reject(createAbortError());
|
|
488
|
+
this.trackEvent({
|
|
489
|
+
type: "abort",
|
|
490
|
+
sourceId: request.sourceId,
|
|
491
|
+
offset: request.offset,
|
|
492
|
+
length: request.length,
|
|
493
|
+
logicalRequestCount: 1,
|
|
494
|
+
logicalBytes: request.length
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
/** Emits one event to Stats and to the optional callback. */
|
|
498
|
+
trackEvent(event) {
|
|
499
|
+
var _a;
|
|
500
|
+
trackStatsEvent(this.stats, event);
|
|
501
|
+
try {
|
|
502
|
+
(_a = this.onEvent) == null ? void 0 : _a.call(this, event);
|
|
503
|
+
} catch {
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/** Emits one batch event after logical requests have been merged. */
|
|
507
|
+
trackBatch(activeRequests, mergedRequests) {
|
|
508
|
+
const logicalBytes = getLogicalRequestBytes(activeRequests);
|
|
509
|
+
const transportBytes = mergedRequests.reduce(
|
|
510
|
+
(sum, mergedRequest) => sum + mergedRequest.endOffset - mergedRequest.offset,
|
|
511
|
+
0
|
|
512
|
+
);
|
|
513
|
+
this.trackEvent({
|
|
514
|
+
type: "batch",
|
|
515
|
+
logicalRequestCount: activeRequests.length,
|
|
516
|
+
transportRequestCount: mergedRequests.length,
|
|
517
|
+
logicalBytes,
|
|
518
|
+
transportBytes,
|
|
519
|
+
overfetchBytes: Math.max(transportBytes - logicalBytes, 0)
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
function trackStatsEvent(stats, event) {
|
|
524
|
+
if ((event.type === "response" || event.type === "error") && event.networkTimeMs !== void 0) {
|
|
525
|
+
stats.get(RANGE_STATS_KEYS.networkTimeMs, "time").addTime(event.networkTimeMs);
|
|
526
|
+
}
|
|
527
|
+
switch (event.type) {
|
|
528
|
+
case "queued":
|
|
529
|
+
stats.get(RANGE_STATS_KEYS.logicalRanges, "count").incrementCount();
|
|
530
|
+
stats.get(RANGE_STATS_KEYS.requestedBytes, "count").addCount(event.logicalBytes || 0);
|
|
531
|
+
break;
|
|
532
|
+
case "batch":
|
|
533
|
+
stats.get(RANGE_STATS_KEYS.rangeBatches, "count").incrementCount();
|
|
534
|
+
stats.get("Logical Ranges Batched", "count").addCount(event.logicalRequestCount || 0);
|
|
535
|
+
stats.get(RANGE_STATS_KEYS.transportRanges, "count").addCount(event.transportRequestCount || 0);
|
|
536
|
+
stats.get(RANGE_STATS_KEYS.coalescedRanges, "count").addCount(
|
|
537
|
+
Math.max((event.logicalRequestCount || 0) - (event.transportRequestCount || 0), 0)
|
|
538
|
+
);
|
|
539
|
+
stats.get(RANGE_STATS_KEYS.transportBytes, "count").addCount(event.transportBytes || 0);
|
|
540
|
+
stats.get(RANGE_STATS_KEYS.overfetchBytes, "count").addCount(event.overfetchBytes || 0);
|
|
541
|
+
break;
|
|
542
|
+
case "response":
|
|
543
|
+
stats.get(RANGE_STATS_KEYS.completedTransportRanges, "count").incrementCount();
|
|
544
|
+
stats.get(RANGE_STATS_KEYS.responseBytes, "count").addCount(event.responseBytes || 0);
|
|
545
|
+
if (event.fullResponse) {
|
|
546
|
+
stats.get(RANGE_STATS_KEYS.fullResponseFallbacks, "count").incrementCount();
|
|
547
|
+
}
|
|
548
|
+
break;
|
|
549
|
+
case "error":
|
|
550
|
+
stats.get(RANGE_STATS_KEYS.failedTransportRanges, "count").incrementCount();
|
|
551
|
+
break;
|
|
552
|
+
case "abort":
|
|
553
|
+
stats.get(RANGE_STATS_KEYS.abortedLogicalRanges, "count").incrementCount();
|
|
554
|
+
break;
|
|
555
|
+
default:
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function createRangeStats(id = "range-request-scheduler") {
|
|
559
|
+
const stats = new import_stats2.Stats({ id });
|
|
560
|
+
initializeStats(stats);
|
|
561
|
+
return stats;
|
|
562
|
+
}
|
|
563
|
+
async function fetchHttpRange(request) {
|
|
564
|
+
const abortContext = createAbortableFetchContext(request.signal);
|
|
565
|
+
const requestStartTime = getTimestamp();
|
|
566
|
+
try {
|
|
567
|
+
let fullResponse = false;
|
|
568
|
+
let response = await request.fetch(
|
|
569
|
+
request.url,
|
|
570
|
+
createRangeFetchOptions(
|
|
571
|
+
request.fetchOptions,
|
|
572
|
+
request.offset,
|
|
573
|
+
request.length,
|
|
574
|
+
abortContext.signal
|
|
575
|
+
)
|
|
576
|
+
);
|
|
577
|
+
if (response.status === 416 && request.offset === 0) {
|
|
578
|
+
const actualLength = parseUnsatisfiedContentRange(response.headers.get("Content-Range"));
|
|
579
|
+
if (!actualLength) {
|
|
580
|
+
throw new Error("Missing content-length on 416 response");
|
|
581
|
+
}
|
|
582
|
+
response = await request.fetch(
|
|
583
|
+
request.url,
|
|
584
|
+
createRangeFetchOptions(request.fetchOptions, 0, actualLength, abortContext.signal)
|
|
585
|
+
);
|
|
586
|
+
fullResponse = true;
|
|
587
|
+
}
|
|
588
|
+
if (response.status === 200) {
|
|
589
|
+
abortContext.abort();
|
|
590
|
+
await cancelIgnoredRangeResponse(response);
|
|
591
|
+
throw new Error("Byte-range request failed: server returned 200 instead of 206");
|
|
592
|
+
}
|
|
593
|
+
if (response.status !== 206 && !response.ok) {
|
|
594
|
+
throw new Error(`Bad response code: ${response.status}`);
|
|
595
|
+
}
|
|
596
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
597
|
+
const contentRangeHeader = response.headers.get("Content-Range");
|
|
598
|
+
const contentRange = parseSatisfiedContentRange(contentRangeHeader);
|
|
599
|
+
if (contentRangeHeader && !contentRange) {
|
|
600
|
+
throw new Error(`Invalid Content-Range header: ${contentRangeHeader}`);
|
|
601
|
+
}
|
|
602
|
+
if (contentRange) {
|
|
603
|
+
const responseLength = contentRange.endOffset - contentRange.offset + 1;
|
|
604
|
+
const requestedEndOffset = request.offset + request.length - 1;
|
|
605
|
+
if (contentRange.offset !== request.offset || contentRange.endOffset > requestedEndOffset || responseLength !== arrayBuffer.byteLength || contentRange.sourceByteLength !== void 0 && contentRange.endOffset >= contentRange.sourceByteLength) {
|
|
606
|
+
throw new Error(`Content-Range does not match requested range: ${contentRangeHeader}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return {
|
|
610
|
+
arrayBuffer,
|
|
611
|
+
status: response.status,
|
|
612
|
+
sourceByteLength: contentRange == null ? void 0 : contentRange.sourceByteLength,
|
|
613
|
+
transportBytes: arrayBuffer.byteLength,
|
|
614
|
+
networkTimeMs: getTimestamp() - requestStartTime,
|
|
615
|
+
fullResponse
|
|
616
|
+
};
|
|
617
|
+
} finally {
|
|
618
|
+
abortContext.removeAbortListener();
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function canMergeRequests(mergedRequest, request, props) {
|
|
622
|
+
if (mergedRequest.sourceId !== request.sourceId) {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
if (mergedRequest.isolationKey !== (request.isolationKey ?? request.sourceId)) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
const requestEndOffset = request.offset + request.length;
|
|
629
|
+
const gapBytes = request.offset - mergedRequest.endOffset;
|
|
630
|
+
const mergedBytes = Math.max(mergedRequest.endOffset, requestEndOffset) - mergedRequest.offset;
|
|
631
|
+
return gapBytes <= props.rangeExpansionBytes && mergedBytes <= props.maxMergedBytes;
|
|
632
|
+
}
|
|
633
|
+
function initializeStats(stats) {
|
|
634
|
+
stats.get(RANGE_STATS_KEYS.logicalRanges, "count");
|
|
635
|
+
stats.get(RANGE_STATS_KEYS.requestedBytes, "count");
|
|
636
|
+
stats.get(RANGE_STATS_KEYS.rangeBatches, "count");
|
|
637
|
+
stats.get("Logical Ranges Batched", "count");
|
|
638
|
+
stats.get(RANGE_STATS_KEYS.transportRanges, "count");
|
|
639
|
+
stats.get(RANGE_STATS_KEYS.coalescedRanges, "count");
|
|
640
|
+
stats.get(RANGE_STATS_KEYS.transportBytes, "count");
|
|
641
|
+
stats.get(RANGE_STATS_KEYS.completedTransportRanges, "count");
|
|
642
|
+
stats.get(RANGE_STATS_KEYS.responseBytes, "count");
|
|
643
|
+
stats.get(RANGE_STATS_KEYS.networkTimeMs, "time");
|
|
644
|
+
stats.get(RANGE_STATS_KEYS.overfetchBytes, "count");
|
|
645
|
+
stats.get(RANGE_STATS_KEYS.fullResponseFallbacks, "count");
|
|
646
|
+
stats.get(RANGE_STATS_KEYS.failedTransportRanges, "count");
|
|
647
|
+
stats.get(RANGE_STATS_KEYS.abortedLogicalRanges, "count");
|
|
648
|
+
}
|
|
649
|
+
function getLogicalRequestBytes(requests) {
|
|
650
|
+
return requests.reduce((sum, request) => sum + request.length, 0);
|
|
651
|
+
}
|
|
652
|
+
function normalizeTransportResult(result) {
|
|
653
|
+
return result instanceof ArrayBuffer ? { arrayBuffer: result } : result;
|
|
654
|
+
}
|
|
655
|
+
function createRangeFetchOptions(fetchOptions, offset, length, signal) {
|
|
656
|
+
return {
|
|
657
|
+
...fetchOptions,
|
|
658
|
+
signal,
|
|
659
|
+
headers: {
|
|
660
|
+
...getHeadersObject(fetchOptions == null ? void 0 : fetchOptions.headers),
|
|
661
|
+
Range: `bytes=${offset}-${offset + length - 1}`
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function getHeadersObject(headers) {
|
|
666
|
+
if (!headers) {
|
|
667
|
+
return {};
|
|
668
|
+
}
|
|
669
|
+
if (headers instanceof Headers) {
|
|
670
|
+
return Object.fromEntries(headers.entries());
|
|
671
|
+
}
|
|
672
|
+
if (Array.isArray(headers)) {
|
|
673
|
+
return Object.fromEntries(headers);
|
|
674
|
+
}
|
|
675
|
+
return headers;
|
|
676
|
+
}
|
|
677
|
+
function parseUnsatisfiedContentRange(contentRange) {
|
|
678
|
+
const match = contentRange == null ? void 0 : contentRange.match(/^bytes \*\/(\d+)$/);
|
|
679
|
+
return match ? Number(match[1]) : null;
|
|
680
|
+
}
|
|
681
|
+
function parseSatisfiedContentRange(contentRange) {
|
|
682
|
+
const match = contentRange == null ? void 0 : contentRange.match(/^bytes (\d+)-(\d+)\/(\d+|\*)$/);
|
|
683
|
+
if (!match) {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
const offset = Number(match[1]);
|
|
687
|
+
const endOffset = Number(match[2]);
|
|
688
|
+
const sourceByteLength = match[3] === "*" ? void 0 : Number(match[3]);
|
|
689
|
+
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(endOffset) || offset > endOffset || sourceByteLength !== void 0 && !Number.isSafeInteger(sourceByteLength)) {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
return { offset, endOffset, sourceByteLength };
|
|
693
|
+
}
|
|
694
|
+
function createAbortableFetchContext(parentSignal) {
|
|
695
|
+
const abortController = new AbortController();
|
|
696
|
+
const abortListener = () => abortController.abort();
|
|
697
|
+
if (parentSignal == null ? void 0 : parentSignal.aborted) {
|
|
698
|
+
abortController.abort();
|
|
699
|
+
} else {
|
|
700
|
+
parentSignal == null ? void 0 : parentSignal.addEventListener("abort", abortListener, { once: true });
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
signal: abortController.signal,
|
|
704
|
+
abort: () => abortController.abort(),
|
|
705
|
+
removeAbortListener: () => parentSignal == null ? void 0 : parentSignal.removeEventListener("abort", abortListener)
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
async function cancelIgnoredRangeResponse(response) {
|
|
709
|
+
var _a;
|
|
710
|
+
await ((_a = response.body) == null ? void 0 : _a.cancel().catch(() => {
|
|
711
|
+
}));
|
|
712
|
+
}
|
|
713
|
+
function getTimestamp() {
|
|
714
|
+
var _a;
|
|
715
|
+
return ((_a = globalThis.performance) == null ? void 0 : _a.now()) ?? Date.now();
|
|
716
|
+
}
|
|
717
|
+
function createAbortError() {
|
|
718
|
+
if (typeof DOMException !== "undefined") {
|
|
719
|
+
return new DOMException("Request aborted", "AbortError");
|
|
720
|
+
}
|
|
721
|
+
const error = new Error("Request aborted");
|
|
722
|
+
error.name = "AbortError";
|
|
723
|
+
return error;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// ../loader-utils/src/lib/path-utils/path.ts
|
|
727
|
+
var path_exports = {};
|
|
728
|
+
__export(path_exports, {
|
|
729
|
+
dirname: () => dirname,
|
|
730
|
+
filename: () => filename,
|
|
731
|
+
join: () => join,
|
|
732
|
+
resolve: () => resolve
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
// ../loader-utils/src/lib/path-utils/get-cwd.ts
|
|
736
|
+
function getCWD() {
|
|
737
|
+
var _a;
|
|
738
|
+
if (typeof process !== "undefined" && typeof process.cwd !== "undefined") {
|
|
739
|
+
return process.cwd();
|
|
740
|
+
}
|
|
741
|
+
const pathname = (_a = window.location) == null ? void 0 : _a.pathname;
|
|
742
|
+
return (pathname == null ? void 0 : pathname.slice(0, pathname.lastIndexOf("/") + 1)) || "";
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// ../loader-utils/src/lib/path-utils/path.ts
|
|
746
|
+
function filename(url) {
|
|
747
|
+
const slashIndex = url ? url.lastIndexOf("/") : -1;
|
|
748
|
+
return slashIndex >= 0 ? url.substr(slashIndex + 1) : url;
|
|
749
|
+
}
|
|
750
|
+
function dirname(url) {
|
|
751
|
+
const slashIndex = url ? url.lastIndexOf("/") : -1;
|
|
752
|
+
return slashIndex >= 0 ? url.substr(0, slashIndex) : "";
|
|
753
|
+
}
|
|
754
|
+
function join(...parts) {
|
|
755
|
+
const separator = "/";
|
|
756
|
+
parts = parts.map((part, index) => {
|
|
757
|
+
if (index) {
|
|
758
|
+
part = part.replace(new RegExp(`^${separator}`), "");
|
|
759
|
+
}
|
|
760
|
+
if (index !== parts.length - 1) {
|
|
761
|
+
part = part.replace(new RegExp(`${separator}$`), "");
|
|
762
|
+
}
|
|
763
|
+
return part;
|
|
764
|
+
});
|
|
765
|
+
return parts.join(separator);
|
|
766
|
+
}
|
|
767
|
+
function resolve(...components) {
|
|
768
|
+
const paths = [];
|
|
769
|
+
for (let _i = 0; _i < components.length; _i++) {
|
|
770
|
+
paths[_i] = components[_i];
|
|
771
|
+
}
|
|
772
|
+
let resolvedPath = "";
|
|
773
|
+
let resolvedAbsolute = false;
|
|
774
|
+
let cwd;
|
|
775
|
+
for (let i = paths.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
|
776
|
+
let path;
|
|
777
|
+
if (i >= 0) {
|
|
778
|
+
path = paths[i];
|
|
779
|
+
} else {
|
|
780
|
+
if (cwd === void 0) {
|
|
781
|
+
cwd = getCWD();
|
|
782
|
+
}
|
|
783
|
+
path = cwd;
|
|
784
|
+
}
|
|
785
|
+
if (path.length === 0) {
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
789
|
+
resolvedAbsolute = path.charCodeAt(0) === SLASH;
|
|
790
|
+
}
|
|
791
|
+
resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute);
|
|
792
|
+
if (resolvedAbsolute) {
|
|
793
|
+
return `/${resolvedPath}`;
|
|
794
|
+
} else if (resolvedPath.length > 0) {
|
|
795
|
+
return resolvedPath;
|
|
796
|
+
}
|
|
797
|
+
return ".";
|
|
798
|
+
}
|
|
799
|
+
var SLASH = 47;
|
|
800
|
+
var DOT = 46;
|
|
801
|
+
function normalizeStringPosix(path, allowAboveRoot) {
|
|
802
|
+
let res = "";
|
|
803
|
+
let lastSlash = -1;
|
|
804
|
+
let dots = 0;
|
|
805
|
+
let code;
|
|
806
|
+
let isAboveRoot = false;
|
|
807
|
+
for (let i = 0; i <= path.length; ++i) {
|
|
808
|
+
if (i < path.length) {
|
|
809
|
+
code = path.charCodeAt(i);
|
|
810
|
+
} else if (code === SLASH) {
|
|
811
|
+
break;
|
|
812
|
+
} else {
|
|
813
|
+
code = SLASH;
|
|
814
|
+
}
|
|
815
|
+
if (code === SLASH) {
|
|
816
|
+
if (lastSlash === i - 1 || dots === 1) {
|
|
817
|
+
} else if (lastSlash !== i - 1 && dots === 2) {
|
|
818
|
+
if (res.length < 2 || !isAboveRoot || res.charCodeAt(res.length - 1) !== DOT || res.charCodeAt(res.length - 2) !== DOT) {
|
|
819
|
+
if (res.length > 2) {
|
|
820
|
+
const start = res.length - 1;
|
|
821
|
+
let j = start;
|
|
822
|
+
for (; j >= 0; --j) {
|
|
823
|
+
if (res.charCodeAt(j) === SLASH) {
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (j !== start) {
|
|
828
|
+
res = j === -1 ? "" : res.slice(0, j);
|
|
829
|
+
lastSlash = i;
|
|
830
|
+
dots = 0;
|
|
831
|
+
isAboveRoot = false;
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
} else if (res.length === 2 || res.length === 1) {
|
|
835
|
+
res = "";
|
|
836
|
+
lastSlash = i;
|
|
837
|
+
dots = 0;
|
|
838
|
+
isAboveRoot = false;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (allowAboveRoot) {
|
|
843
|
+
if (res.length > 0) {
|
|
844
|
+
res += "/..";
|
|
845
|
+
} else {
|
|
846
|
+
res = "..";
|
|
847
|
+
}
|
|
848
|
+
isAboveRoot = true;
|
|
849
|
+
}
|
|
850
|
+
} else {
|
|
851
|
+
const slice = path.slice(lastSlash + 1, i);
|
|
852
|
+
if (res.length > 0) {
|
|
853
|
+
res += `/${slice}`;
|
|
854
|
+
} else {
|
|
855
|
+
res = slice;
|
|
856
|
+
}
|
|
857
|
+
isAboveRoot = false;
|
|
858
|
+
}
|
|
859
|
+
lastSlash = i;
|
|
860
|
+
dots = 0;
|
|
861
|
+
} else if (code === DOT && dots !== -1) {
|
|
862
|
+
++dots;
|
|
863
|
+
} else {
|
|
864
|
+
dots = -1;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return res;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// ../loader-utils/src/lib/files/blob-file.ts
|
|
871
|
+
var BlobFile = class {
|
|
872
|
+
handle;
|
|
873
|
+
size;
|
|
874
|
+
bigsize;
|
|
875
|
+
url;
|
|
876
|
+
constructor(blob) {
|
|
877
|
+
this.handle = blob instanceof ArrayBuffer ? new Blob([blob]) : blob;
|
|
878
|
+
this.size = blob instanceof ArrayBuffer ? blob.byteLength : blob.size;
|
|
879
|
+
this.bigsize = BigInt(this.size);
|
|
880
|
+
this.url = blob instanceof File ? blob.name : "";
|
|
881
|
+
}
|
|
882
|
+
async close() {
|
|
883
|
+
}
|
|
884
|
+
async stat() {
|
|
885
|
+
return {
|
|
886
|
+
size: this.handle.size,
|
|
887
|
+
bigsize: BigInt(this.handle.size),
|
|
888
|
+
isDirectory: false
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
async read(start, length, signal) {
|
|
892
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
893
|
+
throw new Error("Request aborted");
|
|
894
|
+
}
|
|
895
|
+
const arrayBuffer = await this.handle.slice(Number(start), Number(start) + Number(length)).arrayBuffer();
|
|
896
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
897
|
+
throw new Error("Request aborted");
|
|
898
|
+
}
|
|
899
|
+
return arrayBuffer;
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
// ../loader-utils/src/lib/files/http-file-transport.ts
|
|
904
|
+
var HttpFileTransport = class {
|
|
905
|
+
/** Remote object URL. */
|
|
906
|
+
url;
|
|
907
|
+
/** Fetch implementation used for range requests. */
|
|
908
|
+
fetchFunction;
|
|
909
|
+
/** Persistent fetch options copied from the caller. */
|
|
910
|
+
fetchOptions;
|
|
911
|
+
/** Validator enforcement mode. */
|
|
912
|
+
consistency;
|
|
913
|
+
/** Caller-supplied identity information. */
|
|
914
|
+
identityHints;
|
|
915
|
+
/** Failure objects already included in telemetry. */
|
|
916
|
+
countedFailures = /* @__PURE__ */ new WeakSet();
|
|
917
|
+
/** Mutable counters backing immutable telemetry snapshots. */
|
|
918
|
+
telemetry = {
|
|
919
|
+
requestedBytes: 0,
|
|
920
|
+
downloadedBytes: 0,
|
|
921
|
+
requestCount: 0,
|
|
922
|
+
networkTimeMs: 0,
|
|
923
|
+
abortCount: 0,
|
|
924
|
+
errorCount: 0
|
|
925
|
+
};
|
|
926
|
+
/** Creates the transport for one remote object. */
|
|
927
|
+
constructor(url, options) {
|
|
928
|
+
validateKnownByteLength(options.byteLength);
|
|
929
|
+
this.url = url;
|
|
930
|
+
this.fetchFunction = options.fetch || ((input, init) => globalThis.fetch(input, init));
|
|
931
|
+
this.fetchOptions = options.fetchOptions ? { ...options.fetchOptions, headers: new Headers(options.fetchOptions.headers) } : void 0;
|
|
932
|
+
this.consistency = options.consistency || "best-effort";
|
|
933
|
+
this.identityHints = {
|
|
934
|
+
byteLength: options.byteLength,
|
|
935
|
+
etag: normalizeHeaderValue(options.etag),
|
|
936
|
+
lastModified: normalizeHeaderValue(options.lastModified)
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
/** Returns an identity that is complete enough to avoid an opening request. */
|
|
940
|
+
getInitialIdentity() {
|
|
941
|
+
const { byteLength, etag, lastModified } = this.identityHints;
|
|
942
|
+
if (byteLength !== void 0 && (etag !== void 0 || lastModified !== void 0)) {
|
|
943
|
+
return createIdentity(byteLength, etag, lastModified);
|
|
944
|
+
}
|
|
945
|
+
if (byteLength === 0 && this.consistency === "best-effort") {
|
|
946
|
+
return createIdentity(0);
|
|
947
|
+
}
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
/** Returns an immutable point-in-time copy of the transport counters. */
|
|
951
|
+
getTelemetry() {
|
|
952
|
+
return Object.freeze({ ...this.telemetry });
|
|
953
|
+
}
|
|
954
|
+
/** Counts each failure object once even when it passes through several async layers. */
|
|
955
|
+
trackFailure(error) {
|
|
956
|
+
if (typeof error === "object" && error !== null) {
|
|
957
|
+
if (this.countedFailures.has(error)) {
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
this.countedFailures.add(error);
|
|
961
|
+
}
|
|
962
|
+
if (isAbortError(error)) {
|
|
963
|
+
this.telemetry.abortCount++;
|
|
964
|
+
} else {
|
|
965
|
+
this.telemetry.errorCount++;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/** Performs and validates one exact HTTP range request. */
|
|
969
|
+
async requestRange(offset, length, signal, expectedIdentity) {
|
|
970
|
+
var _a;
|
|
971
|
+
const requestStartTime = getTimestamp2();
|
|
972
|
+
const abortContext = createCombinedAbortContext(signal, (_a = this.fetchOptions) == null ? void 0 : _a.signal);
|
|
973
|
+
this.telemetry.requestCount++;
|
|
974
|
+
this.telemetry.requestedBytes += length;
|
|
975
|
+
try {
|
|
976
|
+
const response = await this.fetchFunction(
|
|
977
|
+
this.url,
|
|
978
|
+
createFetchOptions(this.fetchOptions, offset, length, abortContext.signal)
|
|
979
|
+
);
|
|
980
|
+
if (isEmptyIdentityProbeResponse(response, offset, length, expectedIdentity)) {
|
|
981
|
+
return await this.readEmptyIdentityProbe(response, requestStartTime);
|
|
982
|
+
}
|
|
983
|
+
if (response.status !== 206) {
|
|
984
|
+
await cancelResponse(response);
|
|
985
|
+
throw new Error(`HTTP byte-range request expected 206, received ${response.status}`);
|
|
986
|
+
}
|
|
987
|
+
const contentRange = await readContentRange(response);
|
|
988
|
+
const responseIdentity = getResponseIdentity(response, contentRange.byteLength);
|
|
989
|
+
const responseError = getIdentityError(
|
|
990
|
+
expectedIdentity,
|
|
991
|
+
responseIdentity,
|
|
992
|
+
this.identityHints,
|
|
993
|
+
this.consistency
|
|
994
|
+
) || getRangeError(contentRange, offset, length);
|
|
995
|
+
if (responseError) {
|
|
996
|
+
await cancelResponse(response);
|
|
997
|
+
throw new Error(responseError);
|
|
998
|
+
}
|
|
999
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1000
|
+
this.telemetry.downloadedBytes += arrayBuffer.byteLength;
|
|
1001
|
+
if (arrayBuffer.byteLength !== length) {
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
`HTTP byte-range response contained ${arrayBuffer.byteLength} bytes; expected ${length}`
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
return {
|
|
1007
|
+
arrayBuffer,
|
|
1008
|
+
identity: resolveIdentity(expectedIdentity, responseIdentity, this.identityHints),
|
|
1009
|
+
status: response.status,
|
|
1010
|
+
transportBytes: arrayBuffer.byteLength,
|
|
1011
|
+
networkTimeMs: getTimestamp2() - requestStartTime
|
|
1012
|
+
};
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
if (expectedIdentity === null || !isAbortError(error)) {
|
|
1015
|
+
this.trackFailure(error);
|
|
1016
|
+
}
|
|
1017
|
+
throw error;
|
|
1018
|
+
} finally {
|
|
1019
|
+
abortContext.removeAbortListeners();
|
|
1020
|
+
this.telemetry.networkTimeMs += getTimestamp2() - requestStartTime;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
/** Validates and consumes the special empty-object identity response. */
|
|
1024
|
+
async readEmptyIdentityProbe(response, requestStartTime) {
|
|
1025
|
+
const responseIdentity = getResponseIdentity(response, 0);
|
|
1026
|
+
const identityError = getIdentityError(
|
|
1027
|
+
null,
|
|
1028
|
+
responseIdentity,
|
|
1029
|
+
this.identityHints,
|
|
1030
|
+
this.consistency
|
|
1031
|
+
);
|
|
1032
|
+
await cancelResponse(response);
|
|
1033
|
+
if (identityError) {
|
|
1034
|
+
throw new Error(identityError);
|
|
1035
|
+
}
|
|
1036
|
+
return {
|
|
1037
|
+
arrayBuffer: new ArrayBuffer(0),
|
|
1038
|
+
identity: resolveIdentity(null, responseIdentity, this.identityHints),
|
|
1039
|
+
status: response.status,
|
|
1040
|
+
transportBytes: 0,
|
|
1041
|
+
networkTimeMs: getTimestamp2() - requestStartTime
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
function createFetchOptions(fetchOptions, offset, length, signal) {
|
|
1046
|
+
const headers = new Headers(fetchOptions == null ? void 0 : fetchOptions.headers);
|
|
1047
|
+
headers.set("Range", `bytes=${offset}-${offset + length - 1}`);
|
|
1048
|
+
return {
|
|
1049
|
+
...fetchOptions,
|
|
1050
|
+
method: "GET",
|
|
1051
|
+
body: void 0,
|
|
1052
|
+
headers,
|
|
1053
|
+
signal
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
function isEmptyIdentityProbeResponse(response, offset, length, expectedIdentity) {
|
|
1057
|
+
return response.status === 416 && expectedIdentity === null && offset === 0 && length === 1 && /^bytes \*\/0$/i.test(response.headers.get("Content-Range") || "");
|
|
1058
|
+
}
|
|
1059
|
+
async function readContentRange(response) {
|
|
1060
|
+
try {
|
|
1061
|
+
return parseContentRange(response.headers.get("Content-Range"));
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
await cancelResponse(response);
|
|
1064
|
+
throw error;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function parseContentRange(contentRange) {
|
|
1068
|
+
const match = contentRange == null ? void 0 : contentRange.match(/^bytes (\d+)-(\d+)\/(\d+)$/);
|
|
1069
|
+
if (!match) {
|
|
1070
|
+
throw new Error("HTTP byte-range response has an invalid Content-Range header");
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
offset: Number(match[1]),
|
|
1074
|
+
endOffset: Number(match[2]),
|
|
1075
|
+
byteLength: Number(match[3])
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function getResponseIdentity(response, byteLength) {
|
|
1079
|
+
return createIdentity(
|
|
1080
|
+
byteLength,
|
|
1081
|
+
normalizeHeaderValue(response.headers.get("ETag")),
|
|
1082
|
+
normalizeHeaderValue(response.headers.get("Last-Modified"))
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
function resolveIdentity(expectedIdentity, responseIdentity, identityHints) {
|
|
1086
|
+
return expectedIdentity || createIdentity(
|
|
1087
|
+
responseIdentity.byteLength,
|
|
1088
|
+
responseIdentity.etag ?? identityHints.etag,
|
|
1089
|
+
responseIdentity.lastModified ?? identityHints.lastModified
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
function getRangeError(contentRange, offset, length) {
|
|
1093
|
+
if (!Number.isSafeInteger(contentRange.offset) || !Number.isSafeInteger(contentRange.endOffset) || !Number.isSafeInteger(contentRange.byteLength) || contentRange.offset < 0 || contentRange.endOffset < contentRange.offset || contentRange.endOffset >= contentRange.byteLength) {
|
|
1094
|
+
return "HTTP byte-range response has invalid range bounds";
|
|
1095
|
+
}
|
|
1096
|
+
if (contentRange.offset !== offset || contentRange.endOffset !== offset + length - 1) {
|
|
1097
|
+
return "HTTP byte-range response does not match the requested range";
|
|
1098
|
+
}
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
function getIdentityError(expectedIdentity, responseIdentity, identityHints, consistency) {
|
|
1102
|
+
const expectedByteLength = (expectedIdentity == null ? void 0 : expectedIdentity.byteLength) ?? identityHints.byteLength;
|
|
1103
|
+
if (expectedByteLength !== void 0 && responseIdentity.byteLength !== expectedByteLength) {
|
|
1104
|
+
return "HTTP file length changed while the file was open";
|
|
1105
|
+
}
|
|
1106
|
+
const expectedEtag = (expectedIdentity == null ? void 0 : expectedIdentity.etag) ?? identityHints.etag;
|
|
1107
|
+
const expectedLastModified = (expectedIdentity == null ? void 0 : expectedIdentity.lastModified) ?? identityHints.lastModified;
|
|
1108
|
+
if (expectedEtag) {
|
|
1109
|
+
if (responseIdentity.etag) {
|
|
1110
|
+
return expectedEtag === responseIdentity.etag ? null : "HTTP file ETag changed while the file was open";
|
|
1111
|
+
}
|
|
1112
|
+
if (consistency === "strict") {
|
|
1113
|
+
return "HTTP byte-range response is missing the pinned ETag";
|
|
1114
|
+
}
|
|
1115
|
+
if (expectedLastModified && responseIdentity.lastModified && expectedLastModified !== responseIdentity.lastModified) {
|
|
1116
|
+
return "HTTP file Last-Modified value changed while the file was open";
|
|
1117
|
+
}
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
if (expectedLastModified) {
|
|
1121
|
+
if (responseIdentity.lastModified && expectedLastModified !== responseIdentity.lastModified) {
|
|
1122
|
+
return "HTTP file Last-Modified value changed while the file was open";
|
|
1123
|
+
}
|
|
1124
|
+
if (consistency === "strict" && !responseIdentity.lastModified) {
|
|
1125
|
+
return "HTTP byte-range response is missing the pinned Last-Modified value";
|
|
1126
|
+
}
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
if (consistency === "strict" && !responseIdentity.etag && !responseIdentity.lastModified) {
|
|
1130
|
+
return "HTTP byte-range response does not provide an ETag or Last-Modified validator";
|
|
1131
|
+
}
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
function createIdentity(byteLength, etag, lastModified) {
|
|
1135
|
+
return Object.freeze({ byteLength, etag, lastModified });
|
|
1136
|
+
}
|
|
1137
|
+
function validateKnownByteLength(byteLength) {
|
|
1138
|
+
if (byteLength !== void 0 && (!Number.isSafeInteger(byteLength) || byteLength < 0)) {
|
|
1139
|
+
throw new Error("HttpFile byteLength must be a non-negative safe integer");
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
async function cancelResponse(response) {
|
|
1143
|
+
var _a;
|
|
1144
|
+
await ((_a = response.body) == null ? void 0 : _a.cancel().catch(() => {
|
|
1145
|
+
}));
|
|
1146
|
+
}
|
|
1147
|
+
function normalizeHeaderValue(value) {
|
|
1148
|
+
return value || void 0;
|
|
1149
|
+
}
|
|
1150
|
+
function getTimestamp2() {
|
|
1151
|
+
var _a;
|
|
1152
|
+
return ((_a = globalThis.performance) == null ? void 0 : _a.now()) ?? Date.now();
|
|
1153
|
+
}
|
|
1154
|
+
function isAbortError(error) {
|
|
1155
|
+
return error instanceof Error && error.name === "AbortError" || error instanceof Error && /aborted/i.test(error.message);
|
|
1156
|
+
}
|
|
1157
|
+
function createCombinedAbortContext(...signals) {
|
|
1158
|
+
const abortController = new AbortController();
|
|
1159
|
+
const uniqueSignals = [
|
|
1160
|
+
...new Set(signals.filter((signal) => Boolean(signal)))
|
|
1161
|
+
];
|
|
1162
|
+
const abortListener = () => abortController.abort();
|
|
1163
|
+
for (const signal of uniqueSignals) {
|
|
1164
|
+
if (signal.aborted) {
|
|
1165
|
+
abortController.abort();
|
|
1166
|
+
} else {
|
|
1167
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return {
|
|
1171
|
+
signal: abortController.signal,
|
|
1172
|
+
removeAbortListeners: () => {
|
|
1173
|
+
for (const signal of uniqueSignals) {
|
|
1174
|
+
signal.removeEventListener("abort", abortListener);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// ../loader-utils/src/lib/files/http-file.ts
|
|
1181
|
+
var HttpFile = class _HttpFile {
|
|
1182
|
+
/** URL used as the ReadableFile handle. */
|
|
1183
|
+
handle;
|
|
1184
|
+
/** Remote object URL. */
|
|
1185
|
+
url;
|
|
1186
|
+
/** HTTP transport and response validator for this file. */
|
|
1187
|
+
transport;
|
|
1188
|
+
/** Scheduler used to coalesce compatible range reads. */
|
|
1189
|
+
rangeScheduler;
|
|
1190
|
+
/** Unique key that prevents coalescing across transport or credential contexts. */
|
|
1191
|
+
schedulerIsolationKey = {};
|
|
1192
|
+
/** Caller-supplied length available before identity discovery. */
|
|
1193
|
+
suppliedByteLength;
|
|
1194
|
+
/** Pinned remote object identity. */
|
|
1195
|
+
identity;
|
|
1196
|
+
/** Shared opening probe, while identity discovery is in progress. */
|
|
1197
|
+
openPromise = null;
|
|
1198
|
+
/** Whether the file has stopped accepting operations. */
|
|
1199
|
+
closed = false;
|
|
1200
|
+
/** Creates a lazy HTTP file. Use {@link HttpFile.open} when identity must be pinned immediately. */
|
|
1201
|
+
constructor(url, options = {}) {
|
|
1202
|
+
this.handle = url;
|
|
1203
|
+
this.url = url;
|
|
1204
|
+
this.transport = new HttpFileTransport(url, options);
|
|
1205
|
+
this.rangeScheduler = options.rangeScheduler || new RangeRequestScheduler({ batchDelayMs: 0, ...options.rangeSchedulerProps });
|
|
1206
|
+
this.suppliedByteLength = options.byteLength;
|
|
1207
|
+
this.identity = this.transport.getInitialIdentity();
|
|
1208
|
+
}
|
|
1209
|
+
/** Opens a file and pins its byte length and available validators. */
|
|
1210
|
+
static async open(url, options = {}, signal) {
|
|
1211
|
+
const file = new _HttpFile(url, options);
|
|
1212
|
+
await file.open(signal);
|
|
1213
|
+
return file;
|
|
1214
|
+
}
|
|
1215
|
+
/** Pins this file's byte length and available validators. */
|
|
1216
|
+
async open(signal) {
|
|
1217
|
+
this.assertOpen();
|
|
1218
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
1219
|
+
const error = createAbortError2();
|
|
1220
|
+
this.transport.trackFailure(error);
|
|
1221
|
+
throw error;
|
|
1222
|
+
}
|
|
1223
|
+
await this.getIdentity(signal);
|
|
1224
|
+
return this;
|
|
1225
|
+
}
|
|
1226
|
+
/** Pinned file length, or zero before a lazy file has opened. */
|
|
1227
|
+
get size() {
|
|
1228
|
+
var _a;
|
|
1229
|
+
return ((_a = this.identity) == null ? void 0 : _a.byteLength) ?? this.suppliedByteLength ?? 0;
|
|
1230
|
+
}
|
|
1231
|
+
/** Pinned file length as a bigint, or zero before a lazy file has opened. */
|
|
1232
|
+
get bigsize() {
|
|
1233
|
+
return BigInt(this.size);
|
|
1234
|
+
}
|
|
1235
|
+
/** Returns the pinned identity, or null before a lazy file has opened. */
|
|
1236
|
+
getIdentitySnapshot() {
|
|
1237
|
+
return this.identity;
|
|
1238
|
+
}
|
|
1239
|
+
/** Returns an immutable point-in-time copy of this file's transport counters. */
|
|
1240
|
+
getTelemetry() {
|
|
1241
|
+
return this.transport.getTelemetry();
|
|
1242
|
+
}
|
|
1243
|
+
/** Prevents new operations. Active fetches remain controlled by their read signals. */
|
|
1244
|
+
async close() {
|
|
1245
|
+
this.closed = true;
|
|
1246
|
+
}
|
|
1247
|
+
/** Returns pinned file information without issuing a HEAD request. */
|
|
1248
|
+
async stat() {
|
|
1249
|
+
this.assertOpen();
|
|
1250
|
+
const identity = await this.getIdentity();
|
|
1251
|
+
return {
|
|
1252
|
+
size: identity.byteLength,
|
|
1253
|
+
bigsize: BigInt(identity.byteLength),
|
|
1254
|
+
isDirectory: false
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
/** Reads exactly one byte range, with optional cancellation. */
|
|
1258
|
+
async read(offset = 0, length = 0, signal) {
|
|
1259
|
+
this.assertOpen();
|
|
1260
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
1261
|
+
const error = createAbortError2();
|
|
1262
|
+
this.transport.trackFailure(error);
|
|
1263
|
+
throw error;
|
|
1264
|
+
}
|
|
1265
|
+
const identity = await this.getIdentity(signal);
|
|
1266
|
+
const numericOffset = normalizeOffset(offset);
|
|
1267
|
+
validateRange(numericOffset, length, identity.byteLength);
|
|
1268
|
+
if (length === 0) {
|
|
1269
|
+
return new ArrayBuffer(0);
|
|
1270
|
+
}
|
|
1271
|
+
try {
|
|
1272
|
+
return await this.rangeScheduler.scheduleRequest({
|
|
1273
|
+
sourceId: this.url,
|
|
1274
|
+
isolationKey: this.schedulerIsolationKey,
|
|
1275
|
+
offset: numericOffset,
|
|
1276
|
+
length,
|
|
1277
|
+
signal,
|
|
1278
|
+
fetchRange: this.fetchScheduledRange
|
|
1279
|
+
});
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
this.transport.trackFailure(error);
|
|
1282
|
+
throw error;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
/** Reads a range and returns a Response for legacy ReadableFile consumers. */
|
|
1286
|
+
async fetchRange(offset, length, signal) {
|
|
1287
|
+
const numericOffset = normalizeOffset(offset);
|
|
1288
|
+
const arrayBuffer = await this.read(numericOffset, length, signal);
|
|
1289
|
+
const headers = createIdentityHeaders(this.identity, numericOffset, length);
|
|
1290
|
+
return new Response(arrayBuffer, { status: 206, headers });
|
|
1291
|
+
}
|
|
1292
|
+
/** Stable scheduler callback used by every read from this file. */
|
|
1293
|
+
fetchScheduledRange = async (offset, length, signal) => {
|
|
1294
|
+
const identity = await this.getIdentity(signal);
|
|
1295
|
+
const result = await this.transport.requestRange(offset, length, signal, identity);
|
|
1296
|
+
return {
|
|
1297
|
+
arrayBuffer: result.arrayBuffer,
|
|
1298
|
+
status: result.status,
|
|
1299
|
+
transportBytes: result.transportBytes,
|
|
1300
|
+
networkTimeMs: result.networkTimeMs
|
|
1301
|
+
};
|
|
1302
|
+
};
|
|
1303
|
+
/** Returns the shared lazy identity promise, retrying after a failed probe. */
|
|
1304
|
+
getIdentity(signal) {
|
|
1305
|
+
if (this.identity) {
|
|
1306
|
+
return Promise.resolve(this.identity);
|
|
1307
|
+
}
|
|
1308
|
+
if (!this.openPromise) {
|
|
1309
|
+
const nextOpenPromise = this.probeIdentity();
|
|
1310
|
+
const cachedOpenPromise = nextOpenPromise.catch((error) => {
|
|
1311
|
+
if (this.openPromise === cachedOpenPromise) {
|
|
1312
|
+
this.openPromise = null;
|
|
1313
|
+
}
|
|
1314
|
+
throw error;
|
|
1315
|
+
});
|
|
1316
|
+
this.openPromise = cachedOpenPromise;
|
|
1317
|
+
}
|
|
1318
|
+
return waitForPromiseWithSignal(this.openPromise, signal).catch((error) => {
|
|
1319
|
+
this.transport.trackFailure(error);
|
|
1320
|
+
throw error;
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
/** Uses a one-byte GET range to discover object length and validators. */
|
|
1324
|
+
async probeIdentity() {
|
|
1325
|
+
try {
|
|
1326
|
+
const result = await this.transport.requestRange(0, 1, void 0, null);
|
|
1327
|
+
this.identity = result.identity;
|
|
1328
|
+
return result.identity;
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
this.transport.trackFailure(error);
|
|
1331
|
+
throw error;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
/** Throws when the file has been closed. */
|
|
1335
|
+
assertOpen() {
|
|
1336
|
+
if (this.closed) {
|
|
1337
|
+
throw new Error("HttpFile is closed");
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
function createIdentityHeaders(identity, offset, length) {
|
|
1342
|
+
const headers = new Headers();
|
|
1343
|
+
if (length > 0 && identity) {
|
|
1344
|
+
headers.set("Content-Range", `bytes ${offset}-${offset + length - 1}/${identity.byteLength}`);
|
|
1345
|
+
}
|
|
1346
|
+
if (identity == null ? void 0 : identity.etag) {
|
|
1347
|
+
headers.set("ETag", identity.etag);
|
|
1348
|
+
}
|
|
1349
|
+
if (identity == null ? void 0 : identity.lastModified) {
|
|
1350
|
+
headers.set("Last-Modified", identity.lastModified);
|
|
1351
|
+
}
|
|
1352
|
+
return headers;
|
|
1353
|
+
}
|
|
1354
|
+
function normalizeOffset(offset) {
|
|
1355
|
+
const numericOffset = Number(offset);
|
|
1356
|
+
if (!Number.isSafeInteger(numericOffset) || numericOffset < 0) {
|
|
1357
|
+
throw new Error("HTTP byte-range offset must be a non-negative safe integer");
|
|
1358
|
+
}
|
|
1359
|
+
return numericOffset;
|
|
1360
|
+
}
|
|
1361
|
+
function validateRange(offset, length, byteLength) {
|
|
1362
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
1363
|
+
throw new Error("HTTP byte-range length must be a non-negative safe integer");
|
|
1364
|
+
}
|
|
1365
|
+
if (offset + length > byteLength) {
|
|
1366
|
+
throw new Error("HTTP byte range extends beyond the end of the file");
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function createAbortError2() {
|
|
1370
|
+
if (typeof DOMException !== "undefined") {
|
|
1371
|
+
return new DOMException("Request aborted", "AbortError");
|
|
1372
|
+
}
|
|
1373
|
+
const error = new Error("Request aborted");
|
|
1374
|
+
error.name = "AbortError";
|
|
1375
|
+
return error;
|
|
1376
|
+
}
|
|
1377
|
+
function waitForPromiseWithSignal(promise, signal) {
|
|
1378
|
+
if (!signal) {
|
|
1379
|
+
return promise;
|
|
1380
|
+
}
|
|
1381
|
+
if (signal.aborted) {
|
|
1382
|
+
return Promise.reject(createAbortError2());
|
|
1383
|
+
}
|
|
1384
|
+
return new Promise((resolve2, reject) => {
|
|
1385
|
+
const abortListener = () => {
|
|
1386
|
+
signal.removeEventListener("abort", abortListener);
|
|
1387
|
+
reject(createAbortError2());
|
|
1388
|
+
};
|
|
1389
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
1390
|
+
promise.then(
|
|
1391
|
+
(value) => {
|
|
1392
|
+
signal.removeEventListener("abort", abortListener);
|
|
1393
|
+
resolve2(value);
|
|
1394
|
+
},
|
|
1395
|
+
(error) => {
|
|
1396
|
+
signal.removeEventListener("abort", abortListener);
|
|
1397
|
+
reject(error);
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
57
1402
|
|
|
58
1403
|
// dist/utils/doubly-linked-list-node.js
|
|
59
1404
|
var DoublyLinkedListNode = class {
|
|
@@ -181,14 +1526,23 @@ var TilesetCache = class {
|
|
|
181
1526
|
unloadCallback(tileset, tile);
|
|
182
1527
|
}
|
|
183
1528
|
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Unloads least-recently-used tiles until the byte-native cache target is met.
|
|
1531
|
+
*
|
|
1532
|
+
* Tiles touched after the frame sentinel remain protected, so {@link Tileset3D.cacheBytes} is a
|
|
1533
|
+
* soft target rather than a hard cap on the visible working set. Overflow headroom affects
|
|
1534
|
+
* memory-adjusted SSE, not eviction of unused tiles.
|
|
1535
|
+
*
|
|
1536
|
+
* @param tileset - Tileset that owns the cache and reports estimated content memory.
|
|
1537
|
+
* @param unloadCallback - Callback that releases content and decrements the memory estimate.
|
|
1538
|
+
*/
|
|
184
1539
|
unloadTiles(tileset, unloadCallback) {
|
|
185
1540
|
const trimTiles = this._trimTiles;
|
|
186
1541
|
this._trimTiles = false;
|
|
187
1542
|
const list = this._list;
|
|
188
|
-
const maximumMemoryUsageInBytes = tileset.maximumMemoryUsage * 1024 * 1024;
|
|
189
1543
|
const sentinel = this._sentinel;
|
|
190
1544
|
let node = list.head;
|
|
191
|
-
while (node !== sentinel && (tileset.gpuMemoryUsageInBytes >
|
|
1545
|
+
while (node !== sentinel && (tileset.gpuMemoryUsageInBytes > tileset.cacheBytes || trimTiles)) {
|
|
192
1546
|
const tile = node.item;
|
|
193
1547
|
node = node.next;
|
|
194
1548
|
this.unloadTile(tileset, tile, unloadCallback);
|
|
@@ -202,10 +1556,9 @@ var TilesetCache = class {
|
|
|
202
1556
|
// dist/tileset-3d/helpers/transform-utils.js
|
|
203
1557
|
var import_geospatial = require("@math.gl/geospatial");
|
|
204
1558
|
var import_core = require("@math.gl/core");
|
|
205
|
-
var import_loader_utils = require("@loaders.gl/loader-utils");
|
|
206
1559
|
function calculateTransformProps(tileHeader, tile) {
|
|
207
|
-
|
|
208
|
-
|
|
1560
|
+
assert(tileHeader);
|
|
1561
|
+
assert(tile);
|
|
209
1562
|
const { rtcCenter, gltfUpAxis } = tile;
|
|
210
1563
|
const { computedTransform, boundingVolume: { center } } = tileHeader;
|
|
211
1564
|
let modelMatrix = new import_core.Matrix4(computedTransform);
|
|
@@ -272,6 +1625,8 @@ var import_culling = require("@math.gl/culling");
|
|
|
272
1625
|
var import_geospatial2 = require("@math.gl/geospatial");
|
|
273
1626
|
var scratchVector = new import_core2.Vector3();
|
|
274
1627
|
var scratchPosition = new import_core2.Vector3();
|
|
1628
|
+
var CAMERA_POSITION_EPSILON = 1e-5;
|
|
1629
|
+
var CAMERA_DIRECTION_EPSILON = 1e-7;
|
|
275
1630
|
var cullingVolume = new import_culling.CullingVolume([
|
|
276
1631
|
new import_culling.Plane(),
|
|
277
1632
|
new import_culling.Plane(),
|
|
@@ -280,17 +1635,26 @@ var cullingVolume = new import_culling.CullingVolume([
|
|
|
280
1635
|
new import_culling.Plane(),
|
|
281
1636
|
new import_culling.Plane()
|
|
282
1637
|
]);
|
|
283
|
-
function getFrameState(viewport, frameNumber) {
|
|
284
|
-
const {
|
|
1638
|
+
function getFrameState(viewport, frameNumber, options = {}) {
|
|
1639
|
+
const { cameraUp, height } = viewport;
|
|
1640
|
+
const cameraDirection = viewport.cameraDirection;
|
|
285
1641
|
const { metersPerUnit } = viewport.distanceScales;
|
|
286
1642
|
const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
|
|
287
1643
|
const enuToFixedTransform = import_geospatial2.Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
|
|
288
1644
|
const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
|
|
289
1645
|
const cameraPositionCartesian2 = import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, new import_core2.Vector3());
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
1646
|
+
let cameraDirectionCartesian;
|
|
1647
|
+
if (cameraDirection) {
|
|
1648
|
+
cameraDirectionCartesian = new import_core2.Vector3(
|
|
1649
|
+
// @ts-ignore
|
|
1650
|
+
enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraDirection).scale(metersPerUnit))
|
|
1651
|
+
).normalize();
|
|
1652
|
+
} else {
|
|
1653
|
+
const nearPlane = viewport.getFrustumPlanes().near;
|
|
1654
|
+
const nearCenterCommon = closestPointOnPlane(nearPlane, viewport.cameraPosition);
|
|
1655
|
+
const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
|
|
1656
|
+
cameraDirectionCartesian = new import_core2.Vector3(nearCenterCartesian).subtract(cameraPositionCartesian2).normalize();
|
|
1657
|
+
}
|
|
294
1658
|
const cameraUpCartesian = new import_core2.Vector3(
|
|
295
1659
|
// @ts-ignore
|
|
296
1660
|
enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraUp).scale(metersPerUnit))
|
|
@@ -311,7 +1675,10 @@ function getFrameState(viewport, frameNumber) {
|
|
|
311
1675
|
camera: {
|
|
312
1676
|
position: cameraPositionCartesian2,
|
|
313
1677
|
direction: cameraDirectionCartesian,
|
|
314
|
-
up: cameraUpCartesian
|
|
1678
|
+
up: cameraUpCartesian,
|
|
1679
|
+
cartographicPosition: cameraPositionCartographic,
|
|
1680
|
+
verticalFieldOfView: Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0 ? Number(viewport.fovy) * Math.PI / 180 : Math.PI / 3,
|
|
1681
|
+
timeSinceMovement: options.timeSinceCameraMovement ?? Number.POSITIVE_INFINITY
|
|
315
1682
|
},
|
|
316
1683
|
viewport,
|
|
317
1684
|
topDownViewport,
|
|
@@ -319,63 +1686,342 @@ function getFrameState(viewport, frameNumber) {
|
|
|
319
1686
|
cullingVolume,
|
|
320
1687
|
frameNumber,
|
|
321
1688
|
// TODO: This can be the same between updates, what number is unique for between updates?
|
|
322
|
-
sseDenominator: 1.15
|
|
1689
|
+
sseDenominator: 1.15,
|
|
323
1690
|
// Assumes fovy = 60 degrees
|
|
1691
|
+
// Tileset3D fills this immediately before traversal because it depends on the root volume.
|
|
1692
|
+
dynamicScreenSpaceErrorDensity: 0
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
function updateCameraMotionState(previousState, position, direction, currentTime) {
|
|
1696
|
+
const positionArray = Array.from(position);
|
|
1697
|
+
const directionArray = Array.from(direction);
|
|
1698
|
+
const positionChanged = previousState ? positionArray.length !== previousState.position.length || positionArray.some((value, index) => Math.abs(value - previousState.position[index]) > CAMERA_POSITION_EPSILON) : false;
|
|
1699
|
+
const directionChanged = previousState ? directionArray.length !== previousState.direction.length || directionArray.some((value, index) => Math.abs(value - previousState.direction[index]) > CAMERA_DIRECTION_EPSILON) : false;
|
|
1700
|
+
const lastMovementTime = positionChanged || directionChanged ? currentTime : (previousState == null ? void 0 : previousState.lastMovementTime) ?? Number.NEGATIVE_INFINITY;
|
|
1701
|
+
return {
|
|
1702
|
+
state: {
|
|
1703
|
+
position: positionArray,
|
|
1704
|
+
direction: directionArray,
|
|
1705
|
+
lastMovementTime
|
|
1706
|
+
},
|
|
1707
|
+
timeSinceMovement: Math.max((currentTime - lastMovementTime) / 1e3, 0)
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
function limitSelectedTiles(tiles, frameState, maximumTilesSelected) {
|
|
1711
|
+
if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
|
|
1712
|
+
return [tiles, []];
|
|
1713
|
+
}
|
|
1714
|
+
const tuples = [];
|
|
1715
|
+
const { longitude: viewportLongitude, latitude: viewportLatitude } = frameState.viewport;
|
|
1716
|
+
for (const [index, tile] of tiles.entries()) {
|
|
1717
|
+
const [longitude, latitude] = tile.header.mbs;
|
|
1718
|
+
const deltaLon = Math.abs(viewportLongitude - longitude);
|
|
1719
|
+
const deltaLat = Math.abs(viewportLatitude - latitude);
|
|
1720
|
+
const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
|
|
1721
|
+
tuples.push([index, distance]);
|
|
1722
|
+
}
|
|
1723
|
+
const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
|
|
1724
|
+
const selectedTiles = [];
|
|
1725
|
+
for (let i = 0; i < maximumTilesSelected; i++) {
|
|
1726
|
+
selectedTiles.push(tiles[tuplesSorted[i][0]]);
|
|
1727
|
+
}
|
|
1728
|
+
const unselectedTiles = [];
|
|
1729
|
+
for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
|
|
1730
|
+
unselectedTiles.push(tiles[tuplesSorted[i][0]]);
|
|
1731
|
+
}
|
|
1732
|
+
return [selectedTiles, unselectedTiles];
|
|
1733
|
+
}
|
|
1734
|
+
function commonSpacePlanesToWGS84(viewport) {
|
|
1735
|
+
const frustumPlanes = viewport.getFrustumPlanes();
|
|
1736
|
+
const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
|
|
1737
|
+
const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
|
|
1738
|
+
const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
|
|
1739
|
+
let i = 0;
|
|
1740
|
+
cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
|
|
1741
|
+
for (const dir in frustumPlanes) {
|
|
1742
|
+
if (dir === "near") {
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
const plane = frustumPlanes[dir];
|
|
1746
|
+
const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
|
|
1747
|
+
const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
|
|
1748
|
+
cullingVolume.planes[i++].fromPointNormal(
|
|
1749
|
+
cartesianPos,
|
|
1750
|
+
// Want the normal to point into the frustum since that's what culling expects
|
|
1751
|
+
scratchVector.copy(nearCenterCartesian).subtract(cartesianPos)
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function closestPointOnPlane(plane, refPoint, out = new import_core2.Vector3()) {
|
|
1756
|
+
const distanceToRef = plane.normal.dot(refPoint);
|
|
1757
|
+
out.copy(plane.normal).scale(plane.distance - distanceToRef).add(refPoint);
|
|
1758
|
+
return out;
|
|
1759
|
+
}
|
|
1760
|
+
function worldToCartesian(viewport, point, out = new import_core2.Vector3()) {
|
|
1761
|
+
const cartographicPos = viewport.unprojectPosition(point);
|
|
1762
|
+
return import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
// dist/tileset-3d/helpers/tiles-3d-lod.js
|
|
1766
|
+
var import_core3 = require("@math.gl/core");
|
|
1767
|
+
var scratchPositionNormal = new import_core3.Vector3();
|
|
1768
|
+
var scratchCartographic = new import_core3.Vector3();
|
|
1769
|
+
var scratchCenter = new import_core3.Vector3();
|
|
1770
|
+
var scratchPosition2 = new import_core3.Vector3();
|
|
1771
|
+
var scratchDirection = new import_core3.Vector3();
|
|
1772
|
+
var scratchBoxXAxis = new import_core3.Vector3();
|
|
1773
|
+
var scratchBoxYAxis = new import_core3.Vector3();
|
|
1774
|
+
var scratchBoxZAxis = new import_core3.Vector3();
|
|
1775
|
+
var scratchBoxQuaternion = new import_core3.Quaternion();
|
|
1776
|
+
var unitZDirection = new import_core3.Vector3(0, 0, 1);
|
|
1777
|
+
function updateRootTransformForDynamicScreenSpaceError(root, modelMatrix) {
|
|
1778
|
+
root._updateTransform(modelMatrix);
|
|
1779
|
+
}
|
|
1780
|
+
function calculateDynamicScreenSpaceErrorDensity(root, frameState, options) {
|
|
1781
|
+
if (!Number.isFinite(options.dynamicScreenSpaceErrorDensity) || options.dynamicScreenSpaceErrorDensity <= 0) {
|
|
1782
|
+
return 0;
|
|
1783
|
+
}
|
|
1784
|
+
const boundingVolumeHeader = getContentBoundingVolumeHeader(root);
|
|
1785
|
+
const heightRange = boundingVolumeHeader.region ? getRegionHeightRange(boundingVolumeHeader.region, frameState) : getCartesianHeightRange(root, boundingVolumeHeader, frameState);
|
|
1786
|
+
if (!heightRange) {
|
|
1787
|
+
return 0;
|
|
1788
|
+
}
|
|
1789
|
+
const heightFalloff = (0, import_core3.clamp)(options.dynamicScreenSpaceErrorHeightFalloff, 0, 1);
|
|
1790
|
+
const heightClose = heightRange.minimumHeight + (heightRange.maximumHeight - heightRange.minimumHeight) * heightFalloff;
|
|
1791
|
+
const heightSpan = heightRange.maximumHeight - heightClose;
|
|
1792
|
+
const heightPercentage = heightSpan > 0 ? (0, import_core3.clamp)((heightRange.cameraHeight - heightClose) / heightSpan, 0, 1) : heightRange.cameraHeight >= heightRange.maximumHeight ? 1 : 0;
|
|
1793
|
+
const verticalAlignment = (0, import_core3.clamp)(Math.abs(heightRange.cameraDirection.dot(heightRange.upDirection)), 0, 1);
|
|
1794
|
+
const horizonFactor = (1 - verticalAlignment) * (1 - heightPercentage);
|
|
1795
|
+
return options.dynamicScreenSpaceErrorDensity * horizonFactor;
|
|
1796
|
+
}
|
|
1797
|
+
function getDynamicScreenSpaceErrorFog(distanceToCamera, density) {
|
|
1798
|
+
const scalar = distanceToCamera * density;
|
|
1799
|
+
return 1 - Math.exp(-(scalar * scalar));
|
|
1800
|
+
}
|
|
1801
|
+
function getDynamicScreenSpaceError(distanceToCamera, density, factor) {
|
|
1802
|
+
if (density <= 0 || factor <= 0 || !Number.isFinite(density) || !Number.isFinite(factor)) {
|
|
1803
|
+
return 0;
|
|
1804
|
+
}
|
|
1805
|
+
return getDynamicScreenSpaceErrorFog(distanceToCamera, density) * factor;
|
|
1806
|
+
}
|
|
1807
|
+
function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric, viewportHeightFraction = 1) {
|
|
1808
|
+
const tileset = tile.tileset;
|
|
1809
|
+
const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
|
|
1810
|
+
const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
|
|
1811
|
+
if (lodMetricValue === 0) {
|
|
1812
|
+
return 0;
|
|
1813
|
+
}
|
|
1814
|
+
const { viewDistanceScale } = tileset.options;
|
|
1815
|
+
const lodScale = viewDistanceScale || 1;
|
|
1816
|
+
const heightFraction = Number.isFinite(viewportHeightFraction) ? Math.max(viewportHeightFraction, 0) : 1;
|
|
1817
|
+
const orthographicError = getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale, heightFraction);
|
|
1818
|
+
if (orthographicError !== null) {
|
|
1819
|
+
return orthographicError;
|
|
1820
|
+
}
|
|
1821
|
+
const distanceToCamera = Math.max(tile._distanceToCamera, 1e-7);
|
|
1822
|
+
const { height, sseDenominator } = frameState;
|
|
1823
|
+
let screenSpaceError = lodMetricValue * height * heightFraction * lodScale / (distanceToCamera * sseDenominator);
|
|
1824
|
+
if (tileset.options.dynamicScreenSpaceError && !frameState.viewport.orthographic) {
|
|
1825
|
+
screenSpaceError -= getDynamicScreenSpaceError(distanceToCamera, frameState.dynamicScreenSpaceErrorDensity, tileset.options.dynamicScreenSpaceErrorFactor);
|
|
1826
|
+
}
|
|
1827
|
+
return screenSpaceError;
|
|
1828
|
+
}
|
|
1829
|
+
function getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale, viewportHeightFraction) {
|
|
1830
|
+
const viewport = frameState.viewport;
|
|
1831
|
+
if (!(viewport == null ? void 0 : viewport.orthographic)) {
|
|
1832
|
+
return null;
|
|
1833
|
+
}
|
|
1834
|
+
const metersPerPixel = viewport.metersPerPixel;
|
|
1835
|
+
if (!Number.isFinite(metersPerPixel) || !metersPerPixel || metersPerPixel <= 0) {
|
|
1836
|
+
return null;
|
|
1837
|
+
}
|
|
1838
|
+
return lodMetricValue * lodScale * viewportHeightFraction / metersPerPixel;
|
|
1839
|
+
}
|
|
1840
|
+
function getContentBoundingVolumeHeader(root) {
|
|
1841
|
+
var _a;
|
|
1842
|
+
return ((_a = root.header.content) == null ? void 0 : _a.boundingVolume) || root.header.boundingVolume;
|
|
1843
|
+
}
|
|
1844
|
+
function getRegionHeightRange(region, frameState) {
|
|
1845
|
+
const upDirection = scratchPositionNormal.copy(frameState.camera.position).normalize();
|
|
1846
|
+
const cameraDirection = scratchDirection.copy(frameState.camera.direction).normalize();
|
|
1847
|
+
return {
|
|
1848
|
+
cameraHeight: frameState.camera.cartographicPosition[2],
|
|
1849
|
+
minimumHeight: region[4],
|
|
1850
|
+
maximumHeight: region[5],
|
|
1851
|
+
cameraDirection,
|
|
1852
|
+
upDirection
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
function getCartesianHeightRange(root, boundingVolumeHeader, frameState) {
|
|
1856
|
+
const halfHeight = getBoundingVolumeCenterAndHalfHeight(boundingVolumeHeader, scratchCenter);
|
|
1857
|
+
if (halfHeight === null) {
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
const localTransform = new import_core3.Matrix4(root.computedTransform).invert();
|
|
1861
|
+
const ellipsoid = root.tileset.ellipsoid;
|
|
1862
|
+
const centerMagnitude = Math.hypot(scratchCenter[0], scratchCenter[1], scratchCenter[2]);
|
|
1863
|
+
if (centerMagnitude > ellipsoid.minimumRadius) {
|
|
1864
|
+
const centerCartographic = ellipsoid.cartesianToCartographic(scratchCenter, scratchCartographic);
|
|
1865
|
+
const upDirection = scratchPositionNormal.copy(frameState.camera.position).normalize();
|
|
1866
|
+
const cameraDirection = scratchDirection.copy(frameState.camera.direction).normalize();
|
|
1867
|
+
return {
|
|
1868
|
+
cameraHeight: frameState.camera.cartographicPosition[2],
|
|
1869
|
+
minimumHeight: 0,
|
|
1870
|
+
maximumHeight: centerCartographic[2] * 2,
|
|
1871
|
+
cameraDirection,
|
|
1872
|
+
upDirection
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
localTransform.transformAsPoint(frameState.camera.position, scratchPosition2);
|
|
1876
|
+
localTransform.transformAsVector(frameState.camera.direction, scratchDirection);
|
|
1877
|
+
scratchDirection.normalize();
|
|
1878
|
+
return {
|
|
1879
|
+
cameraHeight: scratchPosition2[2],
|
|
1880
|
+
minimumHeight: scratchCenter[2] - halfHeight,
|
|
1881
|
+
maximumHeight: scratchCenter[2] + halfHeight,
|
|
1882
|
+
cameraDirection: scratchDirection,
|
|
1883
|
+
upDirection: unitZDirection
|
|
324
1884
|
};
|
|
325
1885
|
}
|
|
326
|
-
function
|
|
327
|
-
if (
|
|
328
|
-
|
|
1886
|
+
function getBoundingVolumeCenterAndHalfHeight(boundingVolumeHeader, result) {
|
|
1887
|
+
if (boundingVolumeHeader.box) {
|
|
1888
|
+
const box = boundingVolumeHeader.box;
|
|
1889
|
+
result.set(box[0], box[1], box[2]);
|
|
1890
|
+
return getBoxHalfHeight(box);
|
|
329
1891
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const deltaLon = Math.abs(viewportLongitude - longitude);
|
|
335
|
-
const deltaLat = Math.abs(viewportLatitude - latitude);
|
|
336
|
-
const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
|
|
337
|
-
tuples.push([index, distance]);
|
|
1892
|
+
if (boundingVolumeHeader.sphere) {
|
|
1893
|
+
const sphere = boundingVolumeHeader.sphere;
|
|
1894
|
+
result.set(sphere[0], sphere[1], sphere[2]);
|
|
1895
|
+
return sphere[3];
|
|
338
1896
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
1897
|
+
return null;
|
|
1898
|
+
}
|
|
1899
|
+
function getBoxHalfHeight(box) {
|
|
1900
|
+
if (box.length === 10) {
|
|
1901
|
+
scratchBoxQuaternion.fromArray(box, 6);
|
|
1902
|
+
scratchBoxXAxis.set(box[3], 0, 0).transformByQuaternion(scratchBoxQuaternion);
|
|
1903
|
+
scratchBoxYAxis.set(0, box[4], 0).transformByQuaternion(scratchBoxQuaternion);
|
|
1904
|
+
scratchBoxZAxis.set(0, 0, box[5]).transformByQuaternion(scratchBoxQuaternion);
|
|
1905
|
+
return Math.abs(scratchBoxXAxis[2]) + Math.abs(scratchBoxYAxis[2]) + Math.abs(scratchBoxZAxis[2]);
|
|
343
1906
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
1907
|
+
return Math.abs(box[5]) + Math.abs(box[8]) + Math.abs(box[11]);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// dist/tileset-3d/helpers/tiles-3d-request-priority.js
|
|
1911
|
+
var import_core4 = require("@math.gl/core");
|
|
1912
|
+
var import_culling2 = require("@math.gl/culling");
|
|
1913
|
+
|
|
1914
|
+
// dist/constants.js
|
|
1915
|
+
var TILE_CONTENT_STATE = {
|
|
1916
|
+
UNLOADED: 0,
|
|
1917
|
+
// Has never been requested
|
|
1918
|
+
LOADING: 1,
|
|
1919
|
+
// Is waiting on a pending request
|
|
1920
|
+
PROCESSING: 2,
|
|
1921
|
+
// Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
|
|
1922
|
+
READY: 3,
|
|
1923
|
+
// Ready to render.
|
|
1924
|
+
EXPIRED: 4,
|
|
1925
|
+
// Is expired and will be unloaded once new content is loaded.
|
|
1926
|
+
FAILED: 5
|
|
1927
|
+
// Request failed.
|
|
1928
|
+
};
|
|
1929
|
+
var TILE_REFINEMENT;
|
|
1930
|
+
(function(TILE_REFINEMENT2) {
|
|
1931
|
+
TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
|
|
1932
|
+
TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
|
|
1933
|
+
})(TILE_REFINEMENT || (TILE_REFINEMENT = {}));
|
|
1934
|
+
var TILE_TYPE;
|
|
1935
|
+
(function(TILE_TYPE2) {
|
|
1936
|
+
TILE_TYPE2["EMPTY"] = "empty";
|
|
1937
|
+
TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
|
|
1938
|
+
TILE_TYPE2["POINTCLOUD"] = "pointcloud";
|
|
1939
|
+
TILE_TYPE2["MESH"] = "mesh";
|
|
1940
|
+
})(TILE_TYPE || (TILE_TYPE = {}));
|
|
1941
|
+
var TILESET_TYPE;
|
|
1942
|
+
(function(TILESET_TYPE2) {
|
|
1943
|
+
TILESET_TYPE2["I3S"] = "I3S";
|
|
1944
|
+
TILESET_TYPE2["TILES3D"] = "TILES3D";
|
|
1945
|
+
})(TILESET_TYPE || (TILESET_TYPE = {}));
|
|
1946
|
+
var LOD_METRIC_TYPE;
|
|
1947
|
+
(function(LOD_METRIC_TYPE2) {
|
|
1948
|
+
LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
|
|
1949
|
+
LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
|
|
1950
|
+
})(LOD_METRIC_TYPE || (LOD_METRIC_TYPE = {}));
|
|
1951
|
+
var TILE3D_OPTIMIZATION_HINT = {
|
|
1952
|
+
NOT_COMPUTED: -1,
|
|
1953
|
+
USE_OPTIMIZATION: 1,
|
|
1954
|
+
SKIP_OPTIMIZATION: 0
|
|
1955
|
+
};
|
|
1956
|
+
|
|
1957
|
+
// dist/tileset-3d/helpers/tiles-3d-request-priority.js
|
|
1958
|
+
var PREFERRED_SORTING_DIGITS = 1e4;
|
|
1959
|
+
var FOVEATED_SORTING_SCALE = 1e4;
|
|
1960
|
+
var PROGRESSIVE_RESOLUTION_PENALTY = 1e8;
|
|
1961
|
+
var scratchBoundingSphere = new import_culling2.BoundingSphere();
|
|
1962
|
+
var scratchCenterOffset = new import_core4.Vector3();
|
|
1963
|
+
var scratchClosestPointOnLine = new import_core4.Vector3();
|
|
1964
|
+
var scratchVectorToLine = new import_core4.Vector3();
|
|
1965
|
+
var scratchClosestPointOnSphere = new import_core4.Vector3();
|
|
1966
|
+
function interpolateLinearly(minimumValue, maximumValue, interpolationAmount) {
|
|
1967
|
+
return minimumValue + (maximumValue - minimumValue) * interpolationAmount;
|
|
1968
|
+
}
|
|
1969
|
+
function calculateFoveatedFactor(boundingVolume, camera) {
|
|
1970
|
+
const boundingSphere = boundingVolume instanceof import_culling2.BoundingSphere ? boundingVolume : boundingVolume.getBoundingSphere(scratchBoundingSphere);
|
|
1971
|
+
const { center, radius } = boundingSphere;
|
|
1972
|
+
scratchCenterOffset.copy(center).subtract(camera.position);
|
|
1973
|
+
const centerZDepth = Math.max(scratchCenterOffset.dot(camera.direction), 0);
|
|
1974
|
+
scratchClosestPointOnLine.copy(camera.direction).scale(centerZDepth).add(camera.position);
|
|
1975
|
+
scratchVectorToLine.copy(scratchClosestPointOnLine).subtract(center);
|
|
1976
|
+
const distanceToCenterLine = scratchVectorToLine.magnitude();
|
|
1977
|
+
if (distanceToCenterLine <= radius) {
|
|
1978
|
+
return 0;
|
|
347
1979
|
}
|
|
348
|
-
|
|
1980
|
+
scratchClosestPointOnSphere.copy(scratchVectorToLine).normalize().scale(radius).add(center).subtract(camera.position);
|
|
1981
|
+
if (scratchClosestPointOnSphere.magnitude() === 0) {
|
|
1982
|
+
return 0;
|
|
1983
|
+
}
|
|
1984
|
+
scratchClosestPointOnSphere.normalize();
|
|
1985
|
+
return Math.min(Math.max(1 - scratchClosestPointOnSphere.dot(camera.direction), 0), 1);
|
|
349
1986
|
}
|
|
350
|
-
function
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
|
|
357
|
-
for (const dir in frustumPlanes) {
|
|
358
|
-
if (dir === "near") {
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
const plane = frustumPlanes[dir];
|
|
362
|
-
const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
|
|
363
|
-
const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
|
|
364
|
-
cullingVolume.planes[i++].fromPointNormal(
|
|
365
|
-
cartesianPos,
|
|
366
|
-
// Want the normal to point into the frustum since that's what culling expects
|
|
367
|
-
scratchVector.copy(nearCenterCartesian).subtract(cartesianPos)
|
|
368
|
-
);
|
|
1987
|
+
function isProgressiveResolutionPriority(screenSpaceError, parentScreenSpaceError, maximumScreenSpaceError, progressiveResolutionHeightFraction) {
|
|
1988
|
+
if (!Number.isFinite(progressiveResolutionHeightFraction) || progressiveResolutionHeightFraction <= 0 || progressiveResolutionHeightFraction > 0.5) {
|
|
1989
|
+
return false;
|
|
1990
|
+
}
|
|
1991
|
+
if (screenSpaceError > maximumScreenSpaceError) {
|
|
1992
|
+
return true;
|
|
369
1993
|
}
|
|
1994
|
+
return parentScreenSpaceError !== void 0 && parentScreenSpaceError > maximumScreenSpaceError;
|
|
370
1995
|
}
|
|
371
|
-
function
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
1996
|
+
function isFoveatedRequestDeferred(parameters) {
|
|
1997
|
+
const coneSize = Number.isFinite(parameters.foveatedConeSize) ? Math.min(Math.max(parameters.foveatedConeSize, 0), 1) : 1;
|
|
1998
|
+
const replacementRefinement = parameters.refinement === TILE_REFINEMENT.REPLACE;
|
|
1999
|
+
if (replacementRefinement && !parameters.skipLevelOfDetail || !parameters.foveatedScreenSpaceError || coneSize >= 1 || replacementRefinement && parameters.skipLevelOfDetail && parameters.priorityProgressiveResolution) {
|
|
2000
|
+
return false;
|
|
2001
|
+
}
|
|
2002
|
+
const verticalFieldOfView = Number.isFinite(parameters.verticalFieldOfView) && parameters.verticalFieldOfView > 0 ? parameters.verticalFieldOfView : Math.PI / 3;
|
|
2003
|
+
const maximumFoveatedFactor = 1 - Math.cos(verticalFieldOfView * 0.5);
|
|
2004
|
+
const coneFactor = coneSize * maximumFoveatedFactor;
|
|
2005
|
+
if (parameters.foveatedFactor <= coneFactor || maximumFoveatedFactor <= coneFactor) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
const interpolationAmount = Math.min(Math.max((parameters.foveatedFactor - coneFactor) / (maximumFoveatedFactor - coneFactor), 0), 1);
|
|
2009
|
+
const interpolationCallback = typeof parameters.interpolationCallback === "function" ? parameters.interpolationCallback : interpolateLinearly;
|
|
2010
|
+
const interpolatedRelaxation = interpolationCallback(parameters.minimumScreenSpaceErrorRelaxation, parameters.maximumScreenSpaceError, interpolationAmount);
|
|
2011
|
+
const screenSpaceErrorRelaxation = Number.isFinite(interpolatedRelaxation) ? Math.min(Math.max(interpolatedRelaxation, 0), parameters.maximumScreenSpaceError) : 0;
|
|
2012
|
+
const screenSpaceError = parameters.screenSpaceError === 0 && parameters.parentScreenSpaceError !== void 0 ? parameters.parentScreenSpaceError * 0.5 : parameters.screenSpaceError;
|
|
2013
|
+
return parameters.maximumScreenSpaceError - screenSpaceErrorRelaxation <= screenSpaceError;
|
|
375
2014
|
}
|
|
376
|
-
function
|
|
377
|
-
|
|
378
|
-
|
|
2015
|
+
function isFoveatedRequestDelayActive(deferralEligible, timeSinceMovement, foveatedTimeDelay) {
|
|
2016
|
+
return deferralEligible && Number.isFinite(timeSinceMovement) && Number.isFinite(foveatedTimeDelay) && foveatedTimeDelay > 0 && timeSinceMovement < foveatedTimeDelay;
|
|
2017
|
+
}
|
|
2018
|
+
function calculateTileRequestPriority(parameters) {
|
|
2019
|
+
const normalizedReverseScreenSpaceError = parameters.rootScreenSpaceError > 0 ? Math.min(Math.max(parameters.reverseScreenSpaceError / parameters.rootScreenSpaceError, 0), 1) : 0;
|
|
2020
|
+
const preferredSortingDigits = Math.floor(normalizedReverseScreenSpaceError * (PREFERRED_SORTING_DIGITS - 1));
|
|
2021
|
+
const normalizedFoveatedFactor = Number.isFinite(parameters.foveatedFactor) ? Math.min(Math.max(parameters.foveatedFactor, 0), 1) : 0;
|
|
2022
|
+
const foveatedSortingDigits = Math.floor(normalizedFoveatedFactor * (PREFERRED_SORTING_DIGITS - 1)) * FOVEATED_SORTING_SCALE;
|
|
2023
|
+
const progressiveResolutionDigits = parameters.priorityProgressiveResolution ? 0 : PROGRESSIVE_RESOLUTION_PENALTY;
|
|
2024
|
+
return progressiveResolutionDigits + foveatedSortingDigits + preferredSortingDigits;
|
|
379
2025
|
}
|
|
380
2026
|
|
|
381
2027
|
// dist/tileset-3d/common/tileset-source.js
|
|
@@ -384,14 +2030,35 @@ function isTileset3DSource(value) {
|
|
|
384
2030
|
}
|
|
385
2031
|
|
|
386
2032
|
// dist/tileset-3d/common/tileset-3d.js
|
|
387
|
-
var
|
|
2033
|
+
var BYTES_PER_MEBIBYTE = 1024 * 1024;
|
|
2034
|
+
var DEFAULT_CACHE_BYTES = 512 * BYTES_PER_MEBIBYTE;
|
|
2035
|
+
var DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES = 512 * BYTES_PER_MEBIBYTE;
|
|
2036
|
+
var DEFAULT_I3S_CACHE_BYTES = 32 * BYTES_PER_MEBIBYTE;
|
|
2037
|
+
var DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES = BYTES_PER_MEBIBYTE;
|
|
2038
|
+
function validateCacheByteLength(value, optionName) {
|
|
2039
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
2040
|
+
throw new Error(`${optionName} must be a finite number greater than or equal to 0`);
|
|
2041
|
+
}
|
|
2042
|
+
return value;
|
|
2043
|
+
}
|
|
2044
|
+
function resolveCacheBytes(options, fallbackCacheBytes) {
|
|
2045
|
+
const cacheBytes = options.cacheBytes ?? (options.maximumMemoryUsage !== void 0 ? options.maximumMemoryUsage * BYTES_PER_MEBIBYTE : fallbackCacheBytes);
|
|
2046
|
+
return validateCacheByteLength(cacheBytes, "cacheBytes");
|
|
2047
|
+
}
|
|
2048
|
+
function resolveMaximumCacheOverflowBytes(options, fallbackOverflowBytes) {
|
|
2049
|
+
const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? (options.memoryCacheOverflow !== void 0 ? options.memoryCacheOverflow * BYTES_PER_MEBIBYTE : fallbackOverflowBytes);
|
|
2050
|
+
return validateCacheByteLength(maximumCacheOverflowBytes, "maximumCacheOverflowBytes");
|
|
2051
|
+
}
|
|
2052
|
+
var DEFAULT_PROPS3 = {
|
|
388
2053
|
description: "",
|
|
389
2054
|
ellipsoid: import_geospatial3.Ellipsoid.WGS84,
|
|
390
|
-
modelMatrix: new
|
|
2055
|
+
modelMatrix: new import_core5.Matrix4(),
|
|
391
2056
|
throttleRequests: true,
|
|
392
2057
|
maxRequests: 64,
|
|
393
|
-
|
|
394
|
-
|
|
2058
|
+
cacheBytes: DEFAULT_CACHE_BYTES,
|
|
2059
|
+
maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
|
|
2060
|
+
maximumMemoryUsage: DEFAULT_CACHE_BYTES / BYTES_PER_MEBIBYTE,
|
|
2061
|
+
memoryCacheOverflow: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES / BYTES_PER_MEBIBYTE,
|
|
395
2062
|
maximumTilesSelected: 0,
|
|
396
2063
|
debounceTime: 0,
|
|
397
2064
|
onTileLoad: () => {
|
|
@@ -409,8 +2076,18 @@ var DEFAULT_PROPS = {
|
|
|
409
2076
|
},
|
|
410
2077
|
contentLoader: void 0,
|
|
411
2078
|
viewDistanceScale: 1,
|
|
2079
|
+
progressiveResolutionHeightFraction: 0.3,
|
|
2080
|
+
foveatedScreenSpaceError: true,
|
|
2081
|
+
foveatedConeSize: 0.1,
|
|
2082
|
+
foveatedMinimumScreenSpaceErrorRelaxation: 0,
|
|
2083
|
+
foveatedInterpolationCallback: interpolateLinearly,
|
|
2084
|
+
foveatedTimeDelay: 0.2,
|
|
412
2085
|
maximumScreenSpaceError: 8,
|
|
413
|
-
|
|
2086
|
+
dynamicScreenSpaceError: true,
|
|
2087
|
+
dynamicScreenSpaceErrorDensity: 2e-4,
|
|
2088
|
+
dynamicScreenSpaceErrorFactor: 24,
|
|
2089
|
+
dynamicScreenSpaceErrorHeightFalloff: 0.25,
|
|
2090
|
+
memoryAdjustedScreenSpaceError: true,
|
|
414
2091
|
loadTiles: true,
|
|
415
2092
|
updateTransforms: true,
|
|
416
2093
|
viewportTraversersMap: null,
|
|
@@ -459,12 +2136,14 @@ var Tileset3D = class {
|
|
|
459
2136
|
cartesianCenter = null;
|
|
460
2137
|
zoom = 1;
|
|
461
2138
|
boundingVolume = null;
|
|
2139
|
+
/** Effective dynamic SSE density calculated for the most recently traversed viewport. */
|
|
462
2140
|
dynamicScreenSpaceErrorComputedDensity = 0;
|
|
463
|
-
|
|
2141
|
+
/** Estimated bytes currently retained by loaded tile content. */
|
|
464
2142
|
gpuMemoryUsageInBytes = 0;
|
|
2143
|
+
/** Active SSE threshold after optional cache-pressure adjustment, in logical/CSS pixels. */
|
|
465
2144
|
memoryAdjustedScreenSpaceError = 0;
|
|
466
|
-
_cacheBytes =
|
|
467
|
-
|
|
2145
|
+
_cacheBytes = DEFAULT_CACHE_BYTES;
|
|
2146
|
+
_maximumCacheOverflowBytes = DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES;
|
|
468
2147
|
_frameNumber = 0;
|
|
469
2148
|
_tiles = {};
|
|
470
2149
|
_pendingCount = 0;
|
|
@@ -475,6 +2154,10 @@ var Tileset3D = class {
|
|
|
475
2154
|
_requestedTiles = [];
|
|
476
2155
|
_emptyTiles = [];
|
|
477
2156
|
frameStateData = {};
|
|
2157
|
+
/** Last camera pose retained independently for each viewport. */
|
|
2158
|
+
_cameraMotionStates = {};
|
|
2159
|
+
/** Pending traversal that releases requests after camera motion settles. */
|
|
2160
|
+
_deferredTraversalTimeout = null;
|
|
478
2161
|
_traverser;
|
|
479
2162
|
_cache = new TilesetCache();
|
|
480
2163
|
_requestScheduler;
|
|
@@ -487,11 +2170,25 @@ var Tileset3D = class {
|
|
|
487
2170
|
* @param options Traversal and runtime options.
|
|
488
2171
|
*/
|
|
489
2172
|
constructor(source, options) {
|
|
490
|
-
this.options = { ...DEFAULT_PROPS, ...options };
|
|
491
|
-
this.loadOptions = this.options.loadOptions || {};
|
|
492
2173
|
if (!isTileset3DSource(source)) {
|
|
493
2174
|
throw new Error("Tileset3D requires a Tileset3DSource instance");
|
|
494
2175
|
}
|
|
2176
|
+
const suppliedOptions = options || {};
|
|
2177
|
+
const usesTiles3DCacheDefaults = source.type === TILESET_TYPE.TILES3D;
|
|
2178
|
+
const defaultCacheBytes = usesTiles3DCacheDefaults ? DEFAULT_PROPS3.cacheBytes : DEFAULT_I3S_CACHE_BYTES;
|
|
2179
|
+
const defaultMaximumCacheOverflowBytes = usesTiles3DCacheDefaults ? DEFAULT_PROPS3.maximumCacheOverflowBytes : DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES;
|
|
2180
|
+
const cacheBytes = resolveCacheBytes(suppliedOptions, defaultCacheBytes);
|
|
2181
|
+
const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(suppliedOptions, defaultMaximumCacheOverflowBytes);
|
|
2182
|
+
this.options = {
|
|
2183
|
+
...DEFAULT_PROPS3,
|
|
2184
|
+
...suppliedOptions,
|
|
2185
|
+
cacheBytes,
|
|
2186
|
+
maximumCacheOverflowBytes,
|
|
2187
|
+
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
2188
|
+
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE,
|
|
2189
|
+
memoryAdjustedScreenSpaceError: suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
|
|
2190
|
+
};
|
|
2191
|
+
this.loadOptions = this.options.loadOptions || {};
|
|
495
2192
|
this.source = source;
|
|
496
2193
|
this.tileset = null;
|
|
497
2194
|
this.loader = this.source.loader;
|
|
@@ -505,18 +2202,21 @@ var Tileset3D = class {
|
|
|
505
2202
|
this.refine = "";
|
|
506
2203
|
this.contentFormats = this.source.contentFormats;
|
|
507
2204
|
this._traverser = this._initializeTraverser();
|
|
508
|
-
this._requestScheduler = new
|
|
2205
|
+
this._requestScheduler = new RequestScheduler({
|
|
509
2206
|
throttleRequests: this.options.throttleRequests,
|
|
510
2207
|
maxRequests: this.options.maxRequests
|
|
511
2208
|
});
|
|
512
2209
|
this.memoryAdjustedScreenSpaceError = this.options.maximumScreenSpaceError;
|
|
513
|
-
this._cacheBytes =
|
|
514
|
-
this.
|
|
515
|
-
this.stats = new
|
|
2210
|
+
this._cacheBytes = cacheBytes;
|
|
2211
|
+
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
|
|
2212
|
+
this.stats = new import_stats3.Stats({ id: this.url });
|
|
516
2213
|
this._initializeStats();
|
|
517
2214
|
this.tilesetInitializationPromise = this._initializeTileSet();
|
|
518
2215
|
}
|
|
519
2216
|
destroy() {
|
|
2217
|
+
var _a, _b;
|
|
2218
|
+
this._clearDeferredTraversal();
|
|
2219
|
+
(_b = (_a = this.source).destroy) == null ? void 0 : _b.call(_a);
|
|
520
2220
|
this._destroy();
|
|
521
2221
|
}
|
|
522
2222
|
isLoaded() {
|
|
@@ -528,13 +2228,73 @@ var Tileset3D = class {
|
|
|
528
2228
|
get frameNumber() {
|
|
529
2229
|
return this._frameNumber;
|
|
530
2230
|
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
|
|
2233
|
+
*
|
|
2234
|
+
* Current-frame tiles remain protected even when their estimated memory exceeds this target.
|
|
2235
|
+
* Reducing the value makes unused least-recently-used tiles eligible for eviction on the next
|
|
2236
|
+
* traversal.
|
|
2237
|
+
* @default 536870912 for 3D Tiles; 33554432 for I3S
|
|
2238
|
+
*/
|
|
2239
|
+
get cacheBytes() {
|
|
2240
|
+
return this._cacheBytes;
|
|
2241
|
+
}
|
|
2242
|
+
set cacheBytes(value) {
|
|
2243
|
+
this._cacheBytes = validateCacheByteLength(value, "cacheBytes");
|
|
2244
|
+
this.options.cacheBytes = this._cacheBytes;
|
|
2245
|
+
this.options.maximumMemoryUsage = this._cacheBytes / BYTES_PER_MEBIBYTE;
|
|
2246
|
+
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Gets or sets the additional current-frame headroom, in bytes, before memory-adjusted SSE rises.
|
|
2249
|
+
*
|
|
2250
|
+
* The pressure ceiling is `cacheBytes + maximumCacheOverflowBytes`. This value does not change
|
|
2251
|
+
* the base target used to evict tiles that were not touched in the current frame.
|
|
2252
|
+
* @default 536870912 for 3D Tiles; 1048576 for I3S
|
|
2253
|
+
*/
|
|
2254
|
+
get maximumCacheOverflowBytes() {
|
|
2255
|
+
return this._maximumCacheOverflowBytes;
|
|
2256
|
+
}
|
|
2257
|
+
set maximumCacheOverflowBytes(value) {
|
|
2258
|
+
this._maximumCacheOverflowBytes = validateCacheByteLength(value, "maximumCacheOverflowBytes");
|
|
2259
|
+
this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
|
|
2260
|
+
this.options.memoryCacheOverflow = this._maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE;
|
|
2261
|
+
}
|
|
2262
|
+
/**
|
|
2263
|
+
* Gets or sets the soft cache target in mebibytes.
|
|
2264
|
+
* @deprecated Use {@link Tileset3D.cacheBytes}. Assignments remain synchronized for compatibility.
|
|
2265
|
+
*/
|
|
2266
|
+
get maximumMemoryUsage() {
|
|
2267
|
+
return this.cacheBytes / BYTES_PER_MEBIBYTE;
|
|
2268
|
+
}
|
|
2269
|
+
set maximumMemoryUsage(value) {
|
|
2270
|
+
this.cacheBytes = validateCacheByteLength(value * BYTES_PER_MEBIBYTE, "maximumMemoryUsage");
|
|
2271
|
+
}
|
|
531
2272
|
get queryParams() {
|
|
532
2273
|
const rootUrl = this.source.getTileUrl(this.url);
|
|
533
2274
|
const queryIndex = rootUrl.indexOf("?");
|
|
534
2275
|
return queryIndex >= 0 ? rootUrl.slice(queryIndex + 1) : "";
|
|
535
2276
|
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Updates traversal and runtime options, synchronizing byte-native and deprecated cache values.
|
|
2279
|
+
*
|
|
2280
|
+
* For each budget, the byte-native option takes precedence when both unit forms are supplied.
|
|
2281
|
+
* Unspecified budgets retain their current byte values, avoiding repeated MiB conversion.
|
|
2282
|
+
*
|
|
2283
|
+
* @param props - Partial runtime options to apply.
|
|
2284
|
+
*/
|
|
536
2285
|
setProps(props) {
|
|
537
|
-
|
|
2286
|
+
const cacheBytes = resolveCacheBytes(props, this.cacheBytes);
|
|
2287
|
+
const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(props, this.maximumCacheOverflowBytes);
|
|
2288
|
+
this.options = {
|
|
2289
|
+
...this.options,
|
|
2290
|
+
...props,
|
|
2291
|
+
cacheBytes,
|
|
2292
|
+
maximumCacheOverflowBytes,
|
|
2293
|
+
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
2294
|
+
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE
|
|
2295
|
+
};
|
|
2296
|
+
this._cacheBytes = cacheBytes;
|
|
2297
|
+
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
|
|
538
2298
|
}
|
|
539
2299
|
getTileUrl(tilePath) {
|
|
540
2300
|
return this.source.getTileUrl(tilePath);
|
|
@@ -561,22 +2321,29 @@ var Tileset3D = class {
|
|
|
561
2321
|
this.lastUpdatedVieports = viewports;
|
|
562
2322
|
}
|
|
563
2323
|
if (!this.updatePromise) {
|
|
564
|
-
this.updatePromise = new Promise((
|
|
2324
|
+
this.updatePromise = new Promise((resolve2) => {
|
|
565
2325
|
setTimeout(() => {
|
|
566
2326
|
if (this.lastUpdatedVieports) {
|
|
567
2327
|
this.doUpdate(this.lastUpdatedVieports);
|
|
568
2328
|
}
|
|
569
|
-
|
|
2329
|
+
resolve2(this._frameNumber);
|
|
570
2330
|
this.updatePromise = null;
|
|
571
2331
|
}, this.options.debounceTime);
|
|
572
2332
|
});
|
|
573
2333
|
}
|
|
574
2334
|
return this.updatePromise;
|
|
575
2335
|
}
|
|
2336
|
+
/**
|
|
2337
|
+
* Moves the active LOD threshold toward the configured SSE or away from cache pressure.
|
|
2338
|
+
*
|
|
2339
|
+
* Usage below {@link Tileset3D.cacheBytes} restores quality in two-percent steps. Usage above
|
|
2340
|
+
* `cacheBytes + maximumCacheOverflowBytes` reduces refinement in two-percent steps. Values in
|
|
2341
|
+
* the overflow window leave the threshold unchanged, preventing rapid load/evict oscillation.
|
|
2342
|
+
*/
|
|
576
2343
|
adjustScreenSpaceError() {
|
|
577
|
-
if (this.gpuMemoryUsageInBytes < this.
|
|
2344
|
+
if (this.gpuMemoryUsageInBytes < this.cacheBytes) {
|
|
578
2345
|
this.memoryAdjustedScreenSpaceError = Math.max(this.memoryAdjustedScreenSpaceError / 1.02, this.options.maximumScreenSpaceError);
|
|
579
|
-
} else if (this.gpuMemoryUsageInBytes > this.
|
|
2346
|
+
} else if (this.gpuMemoryUsageInBytes > this.cacheBytes + this.maximumCacheOverflowBytes) {
|
|
580
2347
|
this.memoryAdjustedScreenSpaceError *= 1.02;
|
|
581
2348
|
}
|
|
582
2349
|
}
|
|
@@ -591,6 +2358,8 @@ var Tileset3D = class {
|
|
|
591
2358
|
this._cache.reset();
|
|
592
2359
|
this._frameNumber++;
|
|
593
2360
|
this.traverseCounter = preparedViewports.length;
|
|
2361
|
+
const currentTime = Date.now();
|
|
2362
|
+
let minimumDeferredTraversalDelay = Number.POSITIVE_INFINITY;
|
|
594
2363
|
const viewportsToTraverse = [];
|
|
595
2364
|
for (const viewport of preparedViewports) {
|
|
596
2365
|
const id = viewport.id;
|
|
@@ -609,7 +2378,47 @@ var Tileset3D = class {
|
|
|
609
2378
|
continue;
|
|
610
2379
|
}
|
|
611
2380
|
const frameState = getFrameState(viewport, this._frameNumber);
|
|
612
|
-
this.
|
|
2381
|
+
const root = this.roots[id];
|
|
2382
|
+
const cameraMotionUpdate = updateCameraMotionState(this._cameraMotionStates[id], frameState.camera.position, frameState.camera.direction, currentTime);
|
|
2383
|
+
this._cameraMotionStates[id] = cameraMotionUpdate.state;
|
|
2384
|
+
frameState.camera.timeSinceMovement = cameraMotionUpdate.timeSinceMovement;
|
|
2385
|
+
updateRootTransformForDynamicScreenSpaceError(root, this.modelMatrix);
|
|
2386
|
+
frameState.dynamicScreenSpaceErrorDensity = this.type === TILESET_TYPE.TILES3D && this.options.dynamicScreenSpaceError && !frameState.viewport.orthographic ? calculateDynamicScreenSpaceErrorDensity(root, frameState, this.options) : 0;
|
|
2387
|
+
this.dynamicScreenSpaceErrorComputedDensity = frameState.dynamicScreenSpaceErrorDensity;
|
|
2388
|
+
this._traverser.traverse(root, frameState, this.options);
|
|
2389
|
+
if (Object.keys(this._traverser.deferredTiles).length > 0) {
|
|
2390
|
+
minimumDeferredTraversalDelay = Math.min(minimumDeferredTraversalDelay, Math.max(this.options.foveatedTimeDelay - cameraMotionUpdate.timeSinceMovement, 0));
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
if (Number.isFinite(minimumDeferredTraversalDelay)) {
|
|
2394
|
+
this._scheduleDeferredTraversal(minimumDeferredTraversalDelay);
|
|
2395
|
+
} else {
|
|
2396
|
+
this._clearDeferredTraversal();
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Schedules one follow-up traversal when the moving-camera deferral window expires.
|
|
2401
|
+
*
|
|
2402
|
+
* Replacing an existing timer ensures continuing camera movement pushes the retry window forward
|
|
2403
|
+
* instead of creating a queue of redundant traversals.
|
|
2404
|
+
*
|
|
2405
|
+
* @param delayInSeconds - Remaining stationary delay before deferred requests become eligible.
|
|
2406
|
+
*/
|
|
2407
|
+
_scheduleDeferredTraversal(delayInSeconds) {
|
|
2408
|
+
this._clearDeferredTraversal();
|
|
2409
|
+
this._deferredTraversalTimeout = setTimeout(() => {
|
|
2410
|
+
this._deferredTraversalTimeout = null;
|
|
2411
|
+
this.selectTiles().catch((error) => {
|
|
2412
|
+
const traversalError = error instanceof Error ? error : new Error(String(error));
|
|
2413
|
+
this._onTilesetError(traversalError);
|
|
2414
|
+
});
|
|
2415
|
+
}, Math.max(delayInSeconds * 1e3, 0));
|
|
2416
|
+
}
|
|
2417
|
+
/** Clears a pending moving-camera follow-up traversal. */
|
|
2418
|
+
_clearDeferredTraversal() {
|
|
2419
|
+
if (this._deferredTraversalTimeout !== null) {
|
|
2420
|
+
clearTimeout(this._deferredTraversalTimeout);
|
|
2421
|
+
this._deferredTraversalTimeout = null;
|
|
613
2422
|
}
|
|
614
2423
|
}
|
|
615
2424
|
_needTraverse(viewportId) {
|
|
@@ -708,6 +2517,9 @@ var Tileset3D = class {
|
|
|
708
2517
|
}
|
|
709
2518
|
_unloadTiles() {
|
|
710
2519
|
this._cache.unloadTiles(this, (tileset, tile) => tileset._unloadTile(tile));
|
|
2520
|
+
if (this.options.memoryAdjustedScreenSpaceError) {
|
|
2521
|
+
this.adjustScreenSpaceError();
|
|
2522
|
+
}
|
|
711
2523
|
}
|
|
712
2524
|
_updateStats() {
|
|
713
2525
|
let tilesRenderable = 0;
|
|
@@ -792,6 +2604,27 @@ var Tileset3D = class {
|
|
|
792
2604
|
this._onTileLoad(tile, loadResult);
|
|
793
2605
|
}
|
|
794
2606
|
}
|
|
2607
|
+
/**
|
|
2608
|
+
* Loads a source-managed lazy child-header group and reports failures through tile callbacks.
|
|
2609
|
+
*
|
|
2610
|
+
* Subtree metadata shares the normal loading counters but is not reported as render content and
|
|
2611
|
+
* is not added to the GPU cache. Traversal is notified separately after the promise settles.
|
|
2612
|
+
*
|
|
2613
|
+
* @param tile - Runtime tile that owns the lazy subtree reference.
|
|
2614
|
+
* @param frameState - View state that made the subtree eligible for refinement.
|
|
2615
|
+
*/
|
|
2616
|
+
async _loadTileChildren(tile, frameState) {
|
|
2617
|
+
try {
|
|
2618
|
+
this._onStartTileLoading();
|
|
2619
|
+
await tile.loadChildren(frameState);
|
|
2620
|
+
} catch (error) {
|
|
2621
|
+
const tileError = error instanceof Error ? error : new Error("subtree load failed");
|
|
2622
|
+
this._onSourceError(tileError, tile);
|
|
2623
|
+
this._onTileLoadError(tile, tileError);
|
|
2624
|
+
} finally {
|
|
2625
|
+
this._onEndTileLoading();
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
795
2628
|
_onTileLoadError(tile, error) {
|
|
796
2629
|
this.stats.get(TILES_LOAD_FAILED).incrementCount();
|
|
797
2630
|
const message = error.message || error.toString();
|
|
@@ -913,7 +2746,7 @@ var Tileset3D = class {
|
|
|
913
2746
|
this.credits = viewState.credits || this.source.credits || this.credits || { attributions: this.options.attributions || [] };
|
|
914
2747
|
this.description = viewState.description || this.options.description || "";
|
|
915
2748
|
this.boundingVolume = viewState.boundingVolume || ((_a = this.root) == null ? void 0 : _a.boundingVolume) || null;
|
|
916
|
-
this.cartographicCenter = viewState.cartographicCenter || new
|
|
2749
|
+
this.cartographicCenter = viewState.cartographicCenter || new import_core5.Vector3();
|
|
917
2750
|
this.cartesianCenter = viewState.cartesianCenter || null;
|
|
918
2751
|
this.zoom = viewState.zoom || 1;
|
|
919
2752
|
this.geometricError = ((_b = this.tileset) == null ? void 0 : _b.geometricError) || 0;
|
|
@@ -931,7 +2764,6 @@ var Tileset3D = class {
|
|
|
931
2764
|
};
|
|
932
2765
|
|
|
933
2766
|
// dist/tileset-3d/common/indexed-archive-tileset-source.js
|
|
934
|
-
var import_loader_utils3 = require("@loaders.gl/loader-utils");
|
|
935
2767
|
var IndexedArchiveTilesetSource = class {
|
|
936
2768
|
/** URL exposed to the tileset source that owns this archive source. */
|
|
937
2769
|
sourceUrl;
|
|
@@ -998,30 +2830,30 @@ var IndexedArchiveTilesetSource = class {
|
|
|
998
2830
|
async openArchiveReadableFile() {
|
|
999
2831
|
const { data } = this.options;
|
|
1000
2832
|
if (typeof data !== "string") {
|
|
1001
|
-
return new
|
|
2833
|
+
return new BlobFile(data);
|
|
1002
2834
|
}
|
|
1003
2835
|
if (/^https?:\/\//i.test(data)) {
|
|
1004
|
-
return new
|
|
2836
|
+
return new HttpFile(data);
|
|
1005
2837
|
}
|
|
1006
2838
|
const coreApi = this.options.getCoreApi();
|
|
1007
2839
|
if (!coreApi) {
|
|
1008
2840
|
throw new Error(this.options.missingCoreApiMessage);
|
|
1009
2841
|
}
|
|
1010
2842
|
const response = await coreApi.fetchFile(data);
|
|
1011
|
-
return new
|
|
2843
|
+
return new BlobFile(await response.arrayBuffer());
|
|
1012
2844
|
}
|
|
1013
2845
|
resolveArchivePath(request, baseUrl) {
|
|
1014
2846
|
const requestWithoutQuery = stripQueryString(request);
|
|
1015
2847
|
const archiveRoot = stripQueryString(this.sourceUrl);
|
|
1016
2848
|
const baseWithoutQuery = stripQueryString(baseUrl);
|
|
1017
|
-
const basePath = baseWithoutQuery === archiveRoot ? archiveRoot :
|
|
2849
|
+
const basePath = baseWithoutQuery === archiveRoot ? archiveRoot : path_exports.dirname(baseWithoutQuery);
|
|
1018
2850
|
const markerExpression = new RegExp(`\\.${this.options.archiveExtension}/(.*)$`, "i");
|
|
1019
2851
|
const markerMatch = requestWithoutQuery.match(markerExpression);
|
|
1020
2852
|
if (markerMatch == null ? void 0 : markerMatch[1]) {
|
|
1021
2853
|
return markerMatch[1];
|
|
1022
2854
|
}
|
|
1023
2855
|
const isAbsoluteRequest = /^[a-z][a-z0-9+.-]*:/i.test(requestWithoutQuery) || requestWithoutQuery.startsWith("/");
|
|
1024
|
-
const absoluteRequest = requestWithoutQuery.startsWith(archiveRoot) ? requestWithoutQuery : baseWithoutQuery === archiveRoot && !isAbsoluteRequest ? `${archiveRoot}/${requestWithoutQuery}` :
|
|
2856
|
+
const absoluteRequest = requestWithoutQuery.startsWith(archiveRoot) ? requestWithoutQuery : baseWithoutQuery === archiveRoot && !isAbsoluteRequest ? `${archiveRoot}/${requestWithoutQuery}` : path_exports.resolve(basePath, requestWithoutQuery);
|
|
1025
2857
|
if (absoluteRequest === archiveRoot) {
|
|
1026
2858
|
return "";
|
|
1027
2859
|
}
|
|
@@ -1044,70 +2876,26 @@ function stripQueryString(url) {
|
|
|
1044
2876
|
}
|
|
1045
2877
|
|
|
1046
2878
|
// dist/tileset-3d/common/tile-3d.js
|
|
1047
|
-
var
|
|
1048
|
-
var
|
|
1049
|
-
|
|
1050
|
-
// dist/constants.js
|
|
1051
|
-
var TILE_CONTENT_STATE = {
|
|
1052
|
-
UNLOADED: 0,
|
|
1053
|
-
// Has never been requested
|
|
1054
|
-
LOADING: 1,
|
|
1055
|
-
// Is waiting on a pending request
|
|
1056
|
-
PROCESSING: 2,
|
|
1057
|
-
// Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
|
|
1058
|
-
READY: 3,
|
|
1059
|
-
// Ready to render.
|
|
1060
|
-
EXPIRED: 4,
|
|
1061
|
-
// Is expired and will be unloaded once new content is loaded.
|
|
1062
|
-
FAILED: 5
|
|
1063
|
-
// Request failed.
|
|
1064
|
-
};
|
|
1065
|
-
var TILE_REFINEMENT;
|
|
1066
|
-
(function(TILE_REFINEMENT2) {
|
|
1067
|
-
TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
|
|
1068
|
-
TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
|
|
1069
|
-
})(TILE_REFINEMENT || (TILE_REFINEMENT = {}));
|
|
1070
|
-
var TILE_TYPE;
|
|
1071
|
-
(function(TILE_TYPE2) {
|
|
1072
|
-
TILE_TYPE2["EMPTY"] = "empty";
|
|
1073
|
-
TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
|
|
1074
|
-
TILE_TYPE2["POINTCLOUD"] = "pointcloud";
|
|
1075
|
-
TILE_TYPE2["MESH"] = "mesh";
|
|
1076
|
-
})(TILE_TYPE || (TILE_TYPE = {}));
|
|
1077
|
-
var TILESET_TYPE;
|
|
1078
|
-
(function(TILESET_TYPE2) {
|
|
1079
|
-
TILESET_TYPE2["I3S"] = "I3S";
|
|
1080
|
-
TILESET_TYPE2["TILES3D"] = "TILES3D";
|
|
1081
|
-
})(TILESET_TYPE || (TILESET_TYPE = {}));
|
|
1082
|
-
var LOD_METRIC_TYPE;
|
|
1083
|
-
(function(LOD_METRIC_TYPE2) {
|
|
1084
|
-
LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
|
|
1085
|
-
LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
|
|
1086
|
-
})(LOD_METRIC_TYPE || (LOD_METRIC_TYPE = {}));
|
|
1087
|
-
var TILE3D_OPTIMIZATION_HINT = {
|
|
1088
|
-
NOT_COMPUTED: -1,
|
|
1089
|
-
USE_OPTIMIZATION: 1,
|
|
1090
|
-
SKIP_OPTIMIZATION: 0
|
|
1091
|
-
};
|
|
2879
|
+
var import_core8 = require("@math.gl/core");
|
|
2880
|
+
var import_culling4 = require("@math.gl/culling");
|
|
1092
2881
|
|
|
1093
2882
|
// dist/tileset-3d/helpers/bounding-volume.js
|
|
1094
|
-
var
|
|
1095
|
-
var
|
|
2883
|
+
var import_core6 = require("@math.gl/core");
|
|
2884
|
+
var import_culling3 = require("@math.gl/culling");
|
|
1096
2885
|
var import_geospatial4 = require("@math.gl/geospatial");
|
|
1097
|
-
var import_loader_utils4 = require("@loaders.gl/loader-utils");
|
|
1098
2886
|
function defined(x) {
|
|
1099
2887
|
return x !== void 0 && x !== null;
|
|
1100
2888
|
}
|
|
1101
|
-
var scratchPoint = new
|
|
1102
|
-
var scratchScale = new
|
|
1103
|
-
var scratchNorthWest = new
|
|
1104
|
-
var scratchSouthEast = new
|
|
1105
|
-
var
|
|
1106
|
-
var scratchXAxis = new
|
|
1107
|
-
var scratchYAxis = new
|
|
1108
|
-
var scratchZAxis = new
|
|
2889
|
+
var scratchPoint = new import_core6.Vector3();
|
|
2890
|
+
var scratchScale = new import_core6.Vector3();
|
|
2891
|
+
var scratchNorthWest = new import_core6.Vector3();
|
|
2892
|
+
var scratchSouthEast = new import_core6.Vector3();
|
|
2893
|
+
var scratchCenter2 = new import_core6.Vector3();
|
|
2894
|
+
var scratchXAxis = new import_core6.Vector3();
|
|
2895
|
+
var scratchYAxis = new import_core6.Vector3();
|
|
2896
|
+
var scratchZAxis = new import_core6.Vector3();
|
|
1109
2897
|
function createBoundingVolume(boundingVolumeHeader, transform, result) {
|
|
1110
|
-
|
|
2898
|
+
assert(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
|
|
1111
2899
|
if (boundingVolumeHeader.box) {
|
|
1112
2900
|
return createBox(boundingVolumeHeader.box, transform, result);
|
|
1113
2901
|
}
|
|
@@ -1126,8 +2914,8 @@ function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
|
|
|
1126
2914
|
if (boundingVolumeHeader.region) {
|
|
1127
2915
|
const [west, south, east, north, minHeight, maxHeight] = boundingVolumeHeader.region;
|
|
1128
2916
|
return [
|
|
1129
|
-
[(0,
|
|
1130
|
-
[(0,
|
|
2917
|
+
[(0, import_core6.degrees)(west), (0, import_core6.degrees)(south), minHeight],
|
|
2918
|
+
[(0, import_core6.degrees)(east), (0, import_core6.degrees)(north), maxHeight]
|
|
1131
2919
|
];
|
|
1132
2920
|
}
|
|
1133
2921
|
if (boundingVolumeHeader.sphere) {
|
|
@@ -1136,16 +2924,16 @@ function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
|
|
|
1136
2924
|
throw new Error("Unkown boundingVolume type");
|
|
1137
2925
|
}
|
|
1138
2926
|
function createBox(box, transform, result) {
|
|
1139
|
-
const center = new
|
|
2927
|
+
const center = new import_core6.Vector3(box[0], box[1], box[2]);
|
|
1140
2928
|
transform.transform(center, center);
|
|
1141
2929
|
let origin = [];
|
|
1142
2930
|
if (box.length === 10) {
|
|
1143
2931
|
const halfSize = box.slice(3, 6);
|
|
1144
|
-
const quaternion = new
|
|
2932
|
+
const quaternion = new import_core6.Quaternion();
|
|
1145
2933
|
quaternion.fromArray(box, 6);
|
|
1146
|
-
const x = new
|
|
1147
|
-
const y = new
|
|
1148
|
-
const z = new
|
|
2934
|
+
const x = new import_core6.Vector3([1, 0, 0]);
|
|
2935
|
+
const y = new import_core6.Vector3([0, 1, 0]);
|
|
2936
|
+
const z = new import_core6.Vector3([0, 0, 1]);
|
|
1149
2937
|
x.transformByQuaternion(quaternion);
|
|
1150
2938
|
x.scale(halfSize[0]);
|
|
1151
2939
|
y.transformByQuaternion(quaternion);
|
|
@@ -1159,7 +2947,7 @@ function createBox(box, transform, result) {
|
|
|
1159
2947
|
const xAxis = transform.transformAsVector(origin.slice(0, 3));
|
|
1160
2948
|
const yAxis = transform.transformAsVector(origin.slice(3, 6));
|
|
1161
2949
|
const zAxis = transform.transformAsVector(origin.slice(6, 9));
|
|
1162
|
-
const halfAxes = new
|
|
2950
|
+
const halfAxes = new import_core6.Matrix3([
|
|
1163
2951
|
xAxis[0],
|
|
1164
2952
|
xAxis[1],
|
|
1165
2953
|
xAxis[2],
|
|
@@ -1175,10 +2963,10 @@ function createBox(box, transform, result) {
|
|
|
1175
2963
|
result.halfAxes = halfAxes;
|
|
1176
2964
|
return result;
|
|
1177
2965
|
}
|
|
1178
|
-
return new
|
|
2966
|
+
return new import_culling3.OrientedBoundingBox(center, halfAxes);
|
|
1179
2967
|
}
|
|
1180
2968
|
function createSphere(sphere, transform, result) {
|
|
1181
|
-
const center = new
|
|
2969
|
+
const center = new import_core6.Vector3(sphere[0], sphere[1], sphere[2]);
|
|
1182
2970
|
transform.transform(center, center);
|
|
1183
2971
|
const scale = transform.getScale(scratchScale);
|
|
1184
2972
|
const uniformScale = Math.max(Math.max(scale[0], scale[1]), scale[2]);
|
|
@@ -1188,30 +2976,30 @@ function createSphere(sphere, transform, result) {
|
|
|
1188
2976
|
result.radius = radius;
|
|
1189
2977
|
return result;
|
|
1190
2978
|
}
|
|
1191
|
-
return new
|
|
2979
|
+
return new import_culling3.BoundingSphere(center, radius);
|
|
1192
2980
|
}
|
|
1193
2981
|
function createObbFromRegion(region) {
|
|
1194
2982
|
const [west, south, east, north, minHeight, maxHeight] = region;
|
|
1195
|
-
const northWest = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0,
|
|
1196
|
-
const southEast = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0,
|
|
1197
|
-
const centerInCartesian = new
|
|
1198
|
-
import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian,
|
|
1199
|
-
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0,
|
|
1200
|
-
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([
|
|
1201
|
-
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([
|
|
2983
|
+
const northWest = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core6.degrees)(west), (0, import_core6.degrees)(north), minHeight], scratchNorthWest);
|
|
2984
|
+
const southEast = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core6.degrees)(east), (0, import_core6.degrees)(south), maxHeight], scratchSouthEast);
|
|
2985
|
+
const centerInCartesian = new import_core6.Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5);
|
|
2986
|
+
import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter2);
|
|
2987
|
+
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core6.degrees)(east), scratchCenter2[1], scratchCenter2[2]], scratchXAxis);
|
|
2988
|
+
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter2[0], (0, import_core6.degrees)(north), scratchCenter2[2]], scratchYAxis);
|
|
2989
|
+
import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter2[0], scratchCenter2[1], maxHeight], scratchZAxis);
|
|
1202
2990
|
return createBox([
|
|
1203
2991
|
...centerInCartesian,
|
|
1204
2992
|
...scratchXAxis.subtract(centerInCartesian),
|
|
1205
2993
|
...scratchYAxis.subtract(centerInCartesian),
|
|
1206
2994
|
...scratchZAxis.subtract(centerInCartesian)
|
|
1207
|
-
], new
|
|
2995
|
+
], new import_core6.Matrix4());
|
|
1208
2996
|
}
|
|
1209
2997
|
function orientedBoundingBoxToCartographicBounds(boundingVolume) {
|
|
1210
2998
|
const result = emptyCartographicBounds();
|
|
1211
2999
|
const { halfAxes } = boundingVolume;
|
|
1212
|
-
const xAxis = new
|
|
1213
|
-
const yAxis = new
|
|
1214
|
-
const zAxis = new
|
|
3000
|
+
const xAxis = new import_core6.Vector3(halfAxes.getColumn(0));
|
|
3001
|
+
const yAxis = new import_core6.Vector3(halfAxes.getColumn(1));
|
|
3002
|
+
const zAxis = new import_core6.Vector3(halfAxes.getColumn(2));
|
|
1215
3003
|
for (let x = 0; x < 2; x++) {
|
|
1216
3004
|
for (let y = 0; y < 2; y++) {
|
|
1217
3005
|
for (let z = 0; z < 2; z++) {
|
|
@@ -1236,13 +3024,13 @@ function boundingSphereToCartographicBounds(boundingVolume) {
|
|
|
1236
3024
|
if (point) {
|
|
1237
3025
|
zAxis = import_geospatial4.Ellipsoid.WGS84.geodeticSurfaceNormal(point);
|
|
1238
3026
|
} else {
|
|
1239
|
-
zAxis = new
|
|
3027
|
+
zAxis = new import_core6.Vector3(0, 0, 1);
|
|
1240
3028
|
}
|
|
1241
|
-
let xAxis = new
|
|
3029
|
+
let xAxis = new import_core6.Vector3(zAxis[2], -zAxis[1], 0);
|
|
1242
3030
|
if (xAxis.len() > 0) {
|
|
1243
3031
|
xAxis.normalize();
|
|
1244
3032
|
} else {
|
|
1245
|
-
xAxis = new
|
|
3033
|
+
xAxis = new import_core6.Vector3(0, 1, 0);
|
|
1246
3034
|
}
|
|
1247
3035
|
const yAxis = xAxis.clone().cross(zAxis);
|
|
1248
3036
|
for (const axis of [xAxis, yAxis, zAxis]) {
|
|
@@ -1263,77 +3051,25 @@ function emptyCartographicBounds() {
|
|
|
1263
3051
|
];
|
|
1264
3052
|
}
|
|
1265
3053
|
function addToCartographicBounds(target, cartesian) {
|
|
1266
|
-
import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(cartesian, scratchPoint);
|
|
1267
|
-
target[0][0] = Math.min(target[0][0], scratchPoint[0]);
|
|
1268
|
-
target[0][1] = Math.min(target[0][1], scratchPoint[1]);
|
|
1269
|
-
target[0][2] = Math.min(target[0][2], scratchPoint[2]);
|
|
1270
|
-
target[1][0] = Math.max(target[1][0], scratchPoint[0]);
|
|
1271
|
-
target[1][1] = Math.max(target[1][1], scratchPoint[1]);
|
|
1272
|
-
target[1][2] = Math.max(target[1][2], scratchPoint[2]);
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
// dist/tileset-3d/helpers/tiles-3d-lod.js
|
|
1276
|
-
var import_core5 = require("@math.gl/core");
|
|
1277
|
-
var scratchPositionNormal = new import_core5.Vector3();
|
|
1278
|
-
var scratchCartographic = new import_core5.Vector3();
|
|
1279
|
-
var scratchMatrix = new import_core5.Matrix4();
|
|
1280
|
-
var scratchCenter2 = new import_core5.Vector3();
|
|
1281
|
-
var scratchPosition2 = new import_core5.Vector3();
|
|
1282
|
-
var scratchDirection = new import_core5.Vector3();
|
|
1283
|
-
function fog(distanceToCamera, density) {
|
|
1284
|
-
const scalar = distanceToCamera * density;
|
|
1285
|
-
return 1 - Math.exp(-(scalar * scalar));
|
|
1286
|
-
}
|
|
1287
|
-
function getDynamicScreenSpaceError(tileset, distanceToCamera) {
|
|
1288
|
-
if (tileset.dynamicScreenSpaceError && tileset.dynamicScreenSpaceErrorComputedDensity) {
|
|
1289
|
-
const density = tileset.dynamicScreenSpaceErrorComputedDensity;
|
|
1290
|
-
const factor = tileset.dynamicScreenSpaceErrorFactor;
|
|
1291
|
-
const dynamicError = fog(distanceToCamera, density) * factor;
|
|
1292
|
-
return dynamicError;
|
|
1293
|
-
}
|
|
1294
|
-
return 0;
|
|
1295
|
-
}
|
|
1296
|
-
function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric) {
|
|
1297
|
-
const tileset = tile.tileset;
|
|
1298
|
-
const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
|
|
1299
|
-
const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
|
|
1300
|
-
if (lodMetricValue === 0) {
|
|
1301
|
-
return 0;
|
|
1302
|
-
}
|
|
1303
|
-
const { viewDistanceScale } = tileset.options;
|
|
1304
|
-
const lodScale = viewDistanceScale || 1;
|
|
1305
|
-
const orthographicError = getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale);
|
|
1306
|
-
if (orthographicError !== null) {
|
|
1307
|
-
return orthographicError;
|
|
1308
|
-
}
|
|
1309
|
-
const distance = Math.max(tile._distanceToCamera, 1e-7);
|
|
1310
|
-
const { height, sseDenominator } = frameState;
|
|
1311
|
-
let error = lodMetricValue * height * lodScale / (distance * sseDenominator);
|
|
1312
|
-
error -= getDynamicScreenSpaceError(tileset, distance);
|
|
1313
|
-
return error;
|
|
1314
|
-
}
|
|
1315
|
-
function getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale) {
|
|
1316
|
-
const viewport = frameState.viewport;
|
|
1317
|
-
if (!(viewport == null ? void 0 : viewport.orthographic)) {
|
|
1318
|
-
return null;
|
|
1319
|
-
}
|
|
1320
|
-
const metersPerPixel = viewport.metersPerPixel;
|
|
1321
|
-
if (!Number.isFinite(metersPerPixel) || metersPerPixel <= 0) {
|
|
1322
|
-
return null;
|
|
1323
|
-
}
|
|
1324
|
-
return lodMetricValue * lodScale / metersPerPixel;
|
|
3054
|
+
import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(cartesian, scratchPoint);
|
|
3055
|
+
target[0][0] = Math.min(target[0][0], scratchPoint[0]);
|
|
3056
|
+
target[0][1] = Math.min(target[0][1], scratchPoint[1]);
|
|
3057
|
+
target[0][2] = Math.min(target[0][2], scratchPoint[2]);
|
|
3058
|
+
target[1][0] = Math.max(target[1][0], scratchPoint[0]);
|
|
3059
|
+
target[1][1] = Math.max(target[1][1], scratchPoint[1]);
|
|
3060
|
+
target[1][2] = Math.max(target[1][2], scratchPoint[2]);
|
|
1325
3061
|
}
|
|
1326
3062
|
|
|
1327
3063
|
// dist/tileset-3d/helpers/i3s-lod.js
|
|
1328
|
-
var
|
|
3064
|
+
var import_core7 = require("@math.gl/core");
|
|
1329
3065
|
var import_geospatial5 = require("@math.gl/geospatial");
|
|
1330
|
-
var cameraPositionCartesian = new
|
|
1331
|
-
var toEye = new
|
|
1332
|
-
var cameraPositionEnu = new
|
|
1333
|
-
var extraVertexEnu = new
|
|
1334
|
-
var projectedOriginVector = new
|
|
1335
|
-
var enuToCartesianMatrix = new
|
|
1336
|
-
var cartesianToEnuMatrix = new
|
|
3066
|
+
var cameraPositionCartesian = new import_core7.Vector3();
|
|
3067
|
+
var toEye = new import_core7.Vector3();
|
|
3068
|
+
var cameraPositionEnu = new import_core7.Vector3();
|
|
3069
|
+
var extraVertexEnu = new import_core7.Vector3();
|
|
3070
|
+
var projectedOriginVector = new import_core7.Vector3();
|
|
3071
|
+
var enuToCartesianMatrix = new import_core7.Matrix4();
|
|
3072
|
+
var cartesianToEnuMatrix = new import_core7.Matrix4();
|
|
1337
3073
|
function getLodStatus(tile, frameState) {
|
|
1338
3074
|
if (tile.lodMetricValue === 0 || Number.isNaN(tile.lodMetricValue)) {
|
|
1339
3075
|
return "DIG";
|
|
@@ -1377,7 +3113,6 @@ function getProjectedRadius(tile, frameState) {
|
|
|
1377
3113
|
}
|
|
1378
3114
|
|
|
1379
3115
|
// dist/utils/managed-array.js
|
|
1380
|
-
var import_loader_utils5 = require("@loaders.gl/loader-utils");
|
|
1381
3116
|
var ManagedArray = class {
|
|
1382
3117
|
_map = /* @__PURE__ */ new Map();
|
|
1383
3118
|
_array;
|
|
@@ -1418,7 +3153,7 @@ var ManagedArray = class {
|
|
|
1418
3153
|
* @param {Number} index The index to get.
|
|
1419
3154
|
*/
|
|
1420
3155
|
get(index) {
|
|
1421
|
-
|
|
3156
|
+
assert(index < this._array.length);
|
|
1422
3157
|
return this._array[index];
|
|
1423
3158
|
}
|
|
1424
3159
|
/**
|
|
@@ -1428,7 +3163,7 @@ var ManagedArray = class {
|
|
|
1428
3163
|
* @param {*} element The element to set at index.
|
|
1429
3164
|
*/
|
|
1430
3165
|
set(index, element) {
|
|
1431
|
-
|
|
3166
|
+
assert(index >= 0);
|
|
1432
3167
|
if (index >= this.length) {
|
|
1433
3168
|
this.length = index + 1;
|
|
1434
3169
|
}
|
|
@@ -1482,7 +3217,7 @@ var ManagedArray = class {
|
|
|
1482
3217
|
* @param {Number} length The length.
|
|
1483
3218
|
*/
|
|
1484
3219
|
reserve(length) {
|
|
1485
|
-
|
|
3220
|
+
assert(length >= 0);
|
|
1486
3221
|
if (length > this._array.length) {
|
|
1487
3222
|
this._array.length = length;
|
|
1488
3223
|
}
|
|
@@ -1493,7 +3228,7 @@ var ManagedArray = class {
|
|
|
1493
3228
|
* @param {Number} length The length.
|
|
1494
3229
|
*/
|
|
1495
3230
|
resize(length) {
|
|
1496
|
-
|
|
3231
|
+
assert(length >= 0);
|
|
1497
3232
|
this.length = length;
|
|
1498
3233
|
}
|
|
1499
3234
|
/**
|
|
@@ -1518,7 +3253,7 @@ var ManagedArray = class {
|
|
|
1518
3253
|
};
|
|
1519
3254
|
|
|
1520
3255
|
// dist/tileset-3d/common/tileset-traverser.js
|
|
1521
|
-
var
|
|
3256
|
+
var DEFAULT_PROPS4 = {
|
|
1522
3257
|
loadSiblings: false,
|
|
1523
3258
|
skipLevelOfDetail: false,
|
|
1524
3259
|
updateTransforms: true,
|
|
@@ -1535,9 +3270,11 @@ var TilesetTraverser = class {
|
|
|
1535
3270
|
selectedTiles = {};
|
|
1536
3271
|
// tiles should be loaded from server
|
|
1537
3272
|
requestedTiles = {};
|
|
3273
|
+
/** Tiles intentionally held until the camera has remained still for the configured delay. */
|
|
3274
|
+
deferredTiles = {};
|
|
1538
3275
|
// tiles does not have render content
|
|
1539
3276
|
emptyTiles = {};
|
|
1540
|
-
lastUpdate = new Date().getTime();
|
|
3277
|
+
lastUpdate = (/* @__PURE__ */ new Date()).getTime();
|
|
1541
3278
|
updateDebounceTime = 1e3;
|
|
1542
3279
|
/** temporary storage to hold the traversed tiles during a traversal */
|
|
1543
3280
|
_traversalStack = new ManagedArray();
|
|
@@ -1550,7 +3287,7 @@ var TilesetTraverser = class {
|
|
|
1550
3287
|
}
|
|
1551
3288
|
// TODO nested props
|
|
1552
3289
|
constructor(options) {
|
|
1553
|
-
this.options = { ...
|
|
3290
|
+
this.options = { ...DEFAULT_PROPS4, ...options };
|
|
1554
3291
|
}
|
|
1555
3292
|
// tiles should be visible
|
|
1556
3293
|
traverse(root, frameState, options) {
|
|
@@ -1563,6 +3300,7 @@ var TilesetTraverser = class {
|
|
|
1563
3300
|
}
|
|
1564
3301
|
reset() {
|
|
1565
3302
|
this.requestedTiles = {};
|
|
3303
|
+
this.deferredTiles = {};
|
|
1566
3304
|
this.selectedTiles = {};
|
|
1567
3305
|
this.emptyTiles = {};
|
|
1568
3306
|
this._traversalStack.reset();
|
|
@@ -1610,7 +3348,7 @@ var TilesetTraverser = class {
|
|
|
1610
3348
|
this.touchTile(tile, frameState);
|
|
1611
3349
|
tile._shouldRefine = shouldRefine && parentRefines;
|
|
1612
3350
|
}
|
|
1613
|
-
const newTime = new Date().getTime();
|
|
3351
|
+
const newTime = (/* @__PURE__ */ new Date()).getTime();
|
|
1614
3352
|
if (this.traversalFinished(frameState) || newTime - this.lastUpdate > this.updateDebounceTime) {
|
|
1615
3353
|
this.lastUpdate = newTime;
|
|
1616
3354
|
this.options.onTraversalEnd(frameState);
|
|
@@ -1676,11 +3414,28 @@ var TilesetTraverser = class {
|
|
|
1676
3414
|
// tile to load from server
|
|
1677
3415
|
loadTile(tile, frameState) {
|
|
1678
3416
|
if (this.shouldLoadTile(tile)) {
|
|
3417
|
+
if (this.shouldDeferTileRequest(tile)) {
|
|
3418
|
+
this.deferredTiles[tile.id] = tile;
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
1679
3421
|
tile._requestedFrame = frameState.frameNumber;
|
|
1680
3422
|
tile._priority = tile._getPriority();
|
|
1681
3423
|
this.requestedTiles[tile.id] = tile;
|
|
1682
3424
|
}
|
|
1683
3425
|
}
|
|
3426
|
+
/**
|
|
3427
|
+
* Returns whether an eligible peripheral request should wait for camera motion to settle.
|
|
3428
|
+
*
|
|
3429
|
+
* Eligibility and the current time window are calculated together when tile visibility updates.
|
|
3430
|
+
* Keeping the active state on the tile also lets a queued scheduler callback cancel work that
|
|
3431
|
+
* became deferred after its original traversal.
|
|
3432
|
+
*
|
|
3433
|
+
* @param tile - Tile considered for loading.
|
|
3434
|
+
* @returns `true` when the request should be retried after the remaining delay.
|
|
3435
|
+
*/
|
|
3436
|
+
shouldDeferTileRequest(tile) {
|
|
3437
|
+
return tile.priorityDeferred;
|
|
3438
|
+
}
|
|
1684
3439
|
// cache tile
|
|
1685
3440
|
touchTile(tile, frameState) {
|
|
1686
3441
|
tile.tileset._cache.touch(tile);
|
|
@@ -1742,12 +3497,17 @@ var TilesetTraverser = class {
|
|
|
1742
3497
|
// Ignores visibility.
|
|
1743
3498
|
executeEmptyTraversal(root, frameState) {
|
|
1744
3499
|
let allDescendantsLoaded = true;
|
|
3500
|
+
let hasPendingLazyChildren = false;
|
|
1745
3501
|
const stack = this._emptyTraversalStack;
|
|
1746
3502
|
stack.push(root);
|
|
1747
3503
|
while (stack.length > 0) {
|
|
1748
3504
|
const tile = stack.pop();
|
|
1749
3505
|
const traverse = !tile.hasRenderContent && this.canTraverse(tile, frameState);
|
|
1750
3506
|
const emptyLeaf = !tile.hasRenderContent && tile.children.length === 0;
|
|
3507
|
+
if (tile.header.implicitSubtree) {
|
|
3508
|
+
hasPendingLazyChildren = true;
|
|
3509
|
+
allDescendantsLoaded = false;
|
|
3510
|
+
}
|
|
1751
3511
|
if (!traverse && !tile.contentAvailable && !emptyLeaf) {
|
|
1752
3512
|
allDescendantsLoaded = false;
|
|
1753
3513
|
}
|
|
@@ -1763,12 +3523,12 @@ var TilesetTraverser = class {
|
|
|
1763
3523
|
}
|
|
1764
3524
|
}
|
|
1765
3525
|
}
|
|
1766
|
-
return root.hasEmptyContent || allDescendantsLoaded;
|
|
3526
|
+
return root.hasEmptyContent && !hasPendingLazyChildren || allDescendantsLoaded;
|
|
1767
3527
|
}
|
|
1768
3528
|
};
|
|
1769
3529
|
|
|
1770
3530
|
// dist/tileset-3d/common/tile-3d.js
|
|
1771
|
-
var scratchVector2 = new
|
|
3531
|
+
var scratchVector2 = new import_core8.Vector3();
|
|
1772
3532
|
function defined2(x) {
|
|
1773
3533
|
return x !== void 0 && x !== null;
|
|
1774
3534
|
}
|
|
@@ -1805,9 +3565,11 @@ var Tile3D = class {
|
|
|
1805
3565
|
gpuMemoryUsageInBytes = 0;
|
|
1806
3566
|
/** The tile's children - an array of Tile3D objects. */
|
|
1807
3567
|
children = [];
|
|
3568
|
+
/** Current lifecycle state for a lazy implicit subtree rooted at this tile. */
|
|
3569
|
+
childrenState = "ready";
|
|
1808
3570
|
depth = 0;
|
|
1809
3571
|
viewportIds = [];
|
|
1810
|
-
transform = new
|
|
3572
|
+
transform = new import_core8.Matrix4();
|
|
1811
3573
|
extensions = null;
|
|
1812
3574
|
/** TODO Cesium 3d tiles specific */
|
|
1813
3575
|
implicitTiling = null;
|
|
@@ -1823,15 +3585,25 @@ var Tile3D = class {
|
|
|
1823
3585
|
// TODO Cesium 3d tiles specific
|
|
1824
3586
|
_expireDate = null;
|
|
1825
3587
|
_expiredContent = null;
|
|
3588
|
+
/** In-flight source request shared by repeated traversal frames. */
|
|
3589
|
+
_childrenPromise = null;
|
|
1826
3590
|
_boundingBox = void 0;
|
|
1827
3591
|
/** updated every frame for tree traversal and rendering optimizations: */
|
|
1828
3592
|
_distanceToCamera = 0;
|
|
1829
3593
|
_screenSpaceError = 0;
|
|
3594
|
+
/** SSE calculated at the reduced viewport height used to prioritize coarse coverage. */
|
|
3595
|
+
_screenSpaceErrorProgressiveResolution = 0;
|
|
3596
|
+
/** Whether this tile should be promoted into the progressive-resolution request pass. */
|
|
3597
|
+
_priorityProgressiveResolution = false;
|
|
3598
|
+
/** Angular distance from the camera view axis used to prioritize center tiles. */
|
|
3599
|
+
_foveatedFactor = 0;
|
|
3600
|
+
/** Whether this peripheral request is currently inside the camera-motion deferral window. */
|
|
3601
|
+
priorityDeferred = false;
|
|
1830
3602
|
_visibilityPlaneMask;
|
|
1831
3603
|
_visible = void 0;
|
|
1832
3604
|
_contentBoundingVolume;
|
|
1833
3605
|
_viewerRequestVolume;
|
|
1834
|
-
_initialTransform = new
|
|
3606
|
+
_initialTransform = new import_core8.Matrix4();
|
|
1835
3607
|
// Used by traverser, cannot be marked private
|
|
1836
3608
|
_priority = 0;
|
|
1837
3609
|
_selectedFrame = 0;
|
|
@@ -1871,6 +3643,7 @@ var Tile3D = class {
|
|
|
1871
3643
|
this.refine = this._getRefine(header.refine);
|
|
1872
3644
|
this.type = header.type;
|
|
1873
3645
|
this.contentUrl = header.contentUrl;
|
|
3646
|
+
this.childrenState = header.implicitSubtree ? "unloaded" : "ready";
|
|
1874
3647
|
this._initializeLodMetric(header);
|
|
1875
3648
|
this._initializeTransforms(header);
|
|
1876
3649
|
this._updateLodMetricScale();
|
|
@@ -1900,7 +3673,15 @@ var Tile3D = class {
|
|
|
1900
3673
|
}
|
|
1901
3674
|
/** Returns true if tile has children */
|
|
1902
3675
|
get hasChildren() {
|
|
1903
|
-
return this.children.length > 0 || this.header.children && this.header.children.length > 0;
|
|
3676
|
+
return this.children.length > 0 || this.header.children && this.header.children.length > 0 || Boolean(this.header.implicitSubtree && this.childrenState !== "ready");
|
|
3677
|
+
}
|
|
3678
|
+
/** Returns whether traversal may request an unresolved lazy child-header group. */
|
|
3679
|
+
get hasUnloadedChildren() {
|
|
3680
|
+
return Boolean(this.header.implicitSubtree && (this.childrenState === "unloaded" || this.childrenState === "failed"));
|
|
3681
|
+
}
|
|
3682
|
+
/** Returns whether a lazy child-header request is currently in flight. */
|
|
3683
|
+
get childrenLoading() {
|
|
3684
|
+
return this.childrenState === "loading";
|
|
1904
3685
|
}
|
|
1905
3686
|
/**
|
|
1906
3687
|
* Determines if the tile's content is ready. This is automatically `true` for
|
|
@@ -1961,13 +3742,23 @@ var Tile3D = class {
|
|
|
1961
3742
|
}
|
|
1962
3743
|
return this._boundingBox;
|
|
1963
3744
|
}
|
|
1964
|
-
/**
|
|
1965
|
-
|
|
3745
|
+
/**
|
|
3746
|
+
* Gets the tile's screen-space error for normal refinement or a reduced-height priority pass.
|
|
3747
|
+
*
|
|
3748
|
+
* I3S already supplies a screen-oriented LOD metric and intentionally ignores the optional 3D
|
|
3749
|
+
* Tiles progressive-resolution height fraction.
|
|
3750
|
+
*
|
|
3751
|
+
* @param frameState - Current camera and viewport measurements.
|
|
3752
|
+
* @param useParentLodMetric - Whether to substitute the parent LOD metric.
|
|
3753
|
+
* @param viewportHeightFraction - Fraction of viewport height represented by the priority pass.
|
|
3754
|
+
* @returns Screen-space LOD error for the current tileset format.
|
|
3755
|
+
*/
|
|
3756
|
+
getScreenSpaceError(frameState, useParentLodMetric, viewportHeightFraction = 1) {
|
|
1966
3757
|
switch (this.tileset.type) {
|
|
1967
3758
|
case TILESET_TYPE.I3S:
|
|
1968
3759
|
return getProjectedRadius(this, frameState);
|
|
1969
3760
|
case TILESET_TYPE.TILES3D:
|
|
1970
|
-
return getTiles3DScreenSpaceError(this, frameState, useParentLodMetric);
|
|
3761
|
+
return getTiles3DScreenSpaceError(this, frameState, useParentLodMetric, viewportHeightFraction);
|
|
1971
3762
|
default:
|
|
1972
3763
|
throw new Error("Unsupported tileset type");
|
|
1973
3764
|
}
|
|
@@ -1985,12 +3776,34 @@ var Tile3D = class {
|
|
|
1985
3776
|
_getGpuMemoryUsageInBytes() {
|
|
1986
3777
|
return this.content.gpuMemoryUsageInBytes || this.content.byteLength || 0;
|
|
1987
3778
|
}
|
|
1988
|
-
|
|
1989
|
-
*
|
|
1990
|
-
*
|
|
3779
|
+
/**
|
|
3780
|
+
* Returns scheduler priority for render-content requests.
|
|
3781
|
+
*
|
|
3782
|
+
* @returns Numeric priority, or a negative value when the queued request should be cancelled.
|
|
1991
3783
|
*/
|
|
1992
|
-
// eslint-disable-next-line complexity
|
|
1993
3784
|
_getPriority() {
|
|
3785
|
+
return this._calculateRequestPriority(false);
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Returns scheduler priority for lazy child-header requests.
|
|
3789
|
+
*
|
|
3790
|
+
* Contentless implicit placeholders intentionally retain `UNLOADED` content state, so metadata
|
|
3791
|
+
* scheduling skips only that render-content cancellation check and preserves every visibility,
|
|
3792
|
+
* recency, progressive, foveated, and motion-deferral rule.
|
|
3793
|
+
*
|
|
3794
|
+
* @returns Numeric priority, or a negative value when the queued request should be cancelled.
|
|
3795
|
+
*/
|
|
3796
|
+
_getChildrenPriority() {
|
|
3797
|
+
return this._calculateRequestPriority(true);
|
|
3798
|
+
}
|
|
3799
|
+
/**
|
|
3800
|
+
* Calculates the common scheduler priority for content or hierarchy metadata.
|
|
3801
|
+
*
|
|
3802
|
+
* @param allowUnloadedContent - Whether an empty placeholder may request child metadata.
|
|
3803
|
+
* @returns Numeric priority, or a negative value when the queued request should be cancelled.
|
|
3804
|
+
*/
|
|
3805
|
+
// eslint-disable-next-line complexity
|
|
3806
|
+
_calculateRequestPriority(allowUnloadedContent) {
|
|
1994
3807
|
const traverser = this.tileset._traverser;
|
|
1995
3808
|
const { skipLevelOfDetail } = traverser.options;
|
|
1996
3809
|
const maySkipTile = this.refine === TILE_REFINEMENT.ADD || skipLevelOfDetail;
|
|
@@ -2000,14 +3813,23 @@ var Tile3D = class {
|
|
|
2000
3813
|
if (this.tileset._frameNumber - this._touchedFrame >= 1) {
|
|
2001
3814
|
return -1;
|
|
2002
3815
|
}
|
|
2003
|
-
if (this.
|
|
3816
|
+
if (this.priorityDeferred) {
|
|
3817
|
+
return -1;
|
|
3818
|
+
}
|
|
3819
|
+
if (!allowUnloadedContent && this.contentState === TILE_CONTENT_STATE.UNLOADED) {
|
|
2004
3820
|
return -1;
|
|
2005
3821
|
}
|
|
2006
3822
|
const parent = this.parent;
|
|
2007
3823
|
const useParentScreenSpaceError = parent && (!maySkipTile || this._screenSpaceError === 0 || parent.hasTilesetContent);
|
|
2008
3824
|
const screenSpaceError = useParentScreenSpaceError ? parent._screenSpaceError : this._screenSpaceError;
|
|
2009
3825
|
const rootScreenSpaceError = traverser.root ? traverser.root._screenSpaceError : 0;
|
|
2010
|
-
|
|
3826
|
+
const reverseScreenSpaceError = Math.max(rootScreenSpaceError - screenSpaceError, 0);
|
|
3827
|
+
return calculateTileRequestPriority({
|
|
3828
|
+
priorityProgressiveResolution: this._priorityProgressiveResolution,
|
|
3829
|
+
foveatedFactor: this._foveatedFactor,
|
|
3830
|
+
reverseScreenSpaceError,
|
|
3831
|
+
rootScreenSpaceError
|
|
3832
|
+
});
|
|
2011
3833
|
}
|
|
2012
3834
|
/**
|
|
2013
3835
|
* Requests the tile's content.
|
|
@@ -2046,6 +3868,84 @@ var Tile3D = class {
|
|
|
2046
3868
|
requestToken.done();
|
|
2047
3869
|
}
|
|
2048
3870
|
}
|
|
3871
|
+
/**
|
|
3872
|
+
* Requests and installs this tile's lazy child-header group through its source.
|
|
3873
|
+
*
|
|
3874
|
+
* The same request scheduler and SSE/foveated priority value used for render content are reused
|
|
3875
|
+
* for subtree metadata. Repeated traversal frames share one promise, and scheduler cancellation
|
|
3876
|
+
* restores the unloaded state so a later frame can retry.
|
|
3877
|
+
*
|
|
3878
|
+
* @param frameState - View state that made the subtree eligible for refinement.
|
|
3879
|
+
* @returns Source load result, or an unloaded result if scheduling was cancelled.
|
|
3880
|
+
*/
|
|
3881
|
+
async loadChildren(frameState) {
|
|
3882
|
+
if (!this.header.implicitSubtree || this.childrenState === "ready") {
|
|
3883
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
3884
|
+
}
|
|
3885
|
+
if (this._childrenPromise) {
|
|
3886
|
+
return await this._childrenPromise;
|
|
3887
|
+
}
|
|
3888
|
+
if (!this.tileset.source.loadTileChildren) {
|
|
3889
|
+
throw new Error("Tileset source does not support lazy tile children");
|
|
3890
|
+
}
|
|
3891
|
+
this.childrenState = "loading";
|
|
3892
|
+
this._childrenPromise = this._loadChildrenWithScheduler(frameState);
|
|
3893
|
+
try {
|
|
3894
|
+
return await this._childrenPromise;
|
|
3895
|
+
} finally {
|
|
3896
|
+
this._childrenPromise = null;
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
/**
|
|
3900
|
+
* Runs one lazy child-header load after obtaining a scheduler token.
|
|
3901
|
+
*
|
|
3902
|
+
* @param frameState - View state forwarded to the source implementation.
|
|
3903
|
+
* @returns Source load result.
|
|
3904
|
+
*/
|
|
3905
|
+
async _loadChildrenWithScheduler(frameState) {
|
|
3906
|
+
const requestToken = await this.tileset._requestScheduler.scheduleRequest(`${this.id}:implicit-subtree`, this._getChildrenPriority.bind(this));
|
|
3907
|
+
if (!requestToken) {
|
|
3908
|
+
this.childrenState = "unloaded";
|
|
3909
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
3910
|
+
}
|
|
3911
|
+
try {
|
|
3912
|
+
const loadResult = await this.tileset.source.loadTileChildren(this, frameState);
|
|
3913
|
+
this.childrenState = loadResult.loaded ? "ready" : "unloaded";
|
|
3914
|
+
return loadResult;
|
|
3915
|
+
} catch (error) {
|
|
3916
|
+
this.childrenState = "failed";
|
|
3917
|
+
throw error;
|
|
3918
|
+
} finally {
|
|
3919
|
+
requestToken.done();
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
/**
|
|
3923
|
+
* Replaces a contentless implicit placeholder with its materialized subtree-root header.
|
|
3924
|
+
*
|
|
3925
|
+
* Transform state is intentionally preserved: the placeholder already owns the composed
|
|
3926
|
+
* transform, and reapplying the root transform here would double-transform bounds and geometric
|
|
3927
|
+
* error. Content and raw geometric error are reinitialized from the newly available header.
|
|
3928
|
+
*
|
|
3929
|
+
* @param materializedHeader - Available root header produced from one subtree resource.
|
|
3930
|
+
*/
|
|
3931
|
+
applyImplicitSubtreeHeader(materializedHeader) {
|
|
3932
|
+
const existingTransform = this.header.transform;
|
|
3933
|
+
const existingTransformMatrix = this.header.transformMatrix;
|
|
3934
|
+
this.header = {
|
|
3935
|
+
...this.header,
|
|
3936
|
+
...materializedHeader,
|
|
3937
|
+
transform: existingTransform,
|
|
3938
|
+
transformMatrix: existingTransformMatrix,
|
|
3939
|
+
implicitSubtree: void 0
|
|
3940
|
+
};
|
|
3941
|
+
this.refine = this._getRefine(materializedHeader.refine);
|
|
3942
|
+
this.type = materializedHeader.type;
|
|
3943
|
+
this.contentUrl = materializedHeader.contentUrl;
|
|
3944
|
+
this._initializeLodMetric(materializedHeader);
|
|
3945
|
+
this._updateLodMetricScale();
|
|
3946
|
+
this._initializeBoundingVolumes(this.header);
|
|
3947
|
+
this._initializeContent(this.header);
|
|
3948
|
+
}
|
|
2049
3949
|
// Unloads the tile's content.
|
|
2050
3950
|
unloadContent() {
|
|
2051
3951
|
if (this.content && this.content.destroy) {
|
|
@@ -2071,19 +3971,64 @@ var Tile3D = class {
|
|
|
2071
3971
|
return;
|
|
2072
3972
|
}
|
|
2073
3973
|
const parent = this.parent;
|
|
2074
|
-
const parentVisibilityPlaneMask = parent ? parent._visibilityPlaneMask :
|
|
3974
|
+
const parentVisibilityPlaneMask = parent ? parent._visibilityPlaneMask : import_culling4.CullingVolume.MASK_INDETERMINATE;
|
|
2075
3975
|
if (this.tileset._traverser.options.updateTransforms) {
|
|
2076
3976
|
const parentTransform = parent ? parent.computedTransform : this.tileset.modelMatrix;
|
|
2077
3977
|
this._updateTransform(parentTransform);
|
|
2078
3978
|
}
|
|
2079
3979
|
this._distanceToCamera = this.distanceToTile(frameState);
|
|
2080
3980
|
this._screenSpaceError = this.getScreenSpaceError(frameState, false);
|
|
3981
|
+
this._updateRequestPriorityMetrics(frameState);
|
|
2081
3982
|
this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask);
|
|
2082
|
-
this._visible = this._visibilityPlaneMask !==
|
|
3983
|
+
this._visible = this._visibilityPlaneMask !== import_culling4.CullingVolume.MASK_OUTSIDE;
|
|
2083
3984
|
this._inRequestVolume = this.insideViewerRequestVolume(frameState);
|
|
2084
3985
|
this._frameNumber = frameState.frameNumber;
|
|
2085
3986
|
this.viewportIds = viewportIds;
|
|
2086
3987
|
}
|
|
3988
|
+
/**
|
|
3989
|
+
* Updates progressive-resolution and foveated request metrics for a 3D Tiles traversal frame.
|
|
3990
|
+
*
|
|
3991
|
+
* I3S retains its format-specific screen-threshold ordering and is explicitly isolated from
|
|
3992
|
+
* geometric-error scheduling. Orthographic viewports still receive progressive coverage, but
|
|
3993
|
+
* angular foveation is disabled because an orthographic frustum has no perspective field of view.
|
|
3994
|
+
*
|
|
3995
|
+
* @param frameState - Current camera and viewport measurements.
|
|
3996
|
+
*/
|
|
3997
|
+
_updateRequestPriorityMetrics(frameState) {
|
|
3998
|
+
var _a, _b, _c, _d, _e;
|
|
3999
|
+
if (this.tileset.type !== TILESET_TYPE.TILES3D) {
|
|
4000
|
+
this._screenSpaceErrorProgressiveResolution = this._screenSpaceError;
|
|
4001
|
+
this._priorityProgressiveResolution = false;
|
|
4002
|
+
this._foveatedFactor = 0;
|
|
4003
|
+
this.priorityDeferred = false;
|
|
4004
|
+
return;
|
|
4005
|
+
}
|
|
4006
|
+
const options = this.tileset.options;
|
|
4007
|
+
const progressiveResolutionHeightFraction = options.progressiveResolutionHeightFraction;
|
|
4008
|
+
this._screenSpaceErrorProgressiveResolution = this.getScreenSpaceError(frameState, false, progressiveResolutionHeightFraction);
|
|
4009
|
+
this._priorityProgressiveResolution = isProgressiveResolutionPriority(this._screenSpaceErrorProgressiveResolution, (_a = this.parent) == null ? void 0 : _a._screenSpaceErrorProgressiveResolution, this.tileset.memoryAdjustedScreenSpaceError, progressiveResolutionHeightFraction);
|
|
4010
|
+
if (!options.foveatedScreenSpaceError || ((_b = frameState.viewport) == null ? void 0 : _b.orthographic) || !((_c = frameState.camera) == null ? void 0 : _c.position) || !((_d = frameState.camera) == null ? void 0 : _d.direction)) {
|
|
4011
|
+
this._foveatedFactor = 0;
|
|
4012
|
+
this.priorityDeferred = false;
|
|
4013
|
+
return;
|
|
4014
|
+
}
|
|
4015
|
+
this._foveatedFactor = calculateFoveatedFactor(this.boundingVolume, frameState.camera);
|
|
4016
|
+
const deferralEligible = isFoveatedRequestDeferred({
|
|
4017
|
+
refinement: this.refine,
|
|
4018
|
+
skipLevelOfDetail: this.tileset._traverser.options.skipLevelOfDetail,
|
|
4019
|
+
foveatedScreenSpaceError: options.foveatedScreenSpaceError,
|
|
4020
|
+
foveatedConeSize: options.foveatedConeSize,
|
|
4021
|
+
minimumScreenSpaceErrorRelaxation: options.foveatedMinimumScreenSpaceErrorRelaxation,
|
|
4022
|
+
interpolationCallback: options.foveatedInterpolationCallback,
|
|
4023
|
+
foveatedFactor: this._foveatedFactor,
|
|
4024
|
+
verticalFieldOfView: frameState.camera.verticalFieldOfView,
|
|
4025
|
+
screenSpaceError: this._screenSpaceError,
|
|
4026
|
+
parentScreenSpaceError: (_e = this.parent) == null ? void 0 : _e._screenSpaceError,
|
|
4027
|
+
maximumScreenSpaceError: this.tileset.memoryAdjustedScreenSpaceError,
|
|
4028
|
+
priorityProgressiveResolution: this._priorityProgressiveResolution
|
|
4029
|
+
});
|
|
4030
|
+
this.priorityDeferred = isFoveatedRequestDelayActive(deferralEligible, frameState.camera.timeSinceMovement, options.foveatedTimeDelay);
|
|
4031
|
+
}
|
|
2087
4032
|
// Determines whether the tile's bounding volume intersects the culling volume.
|
|
2088
4033
|
// @param {FrameState} frameState The frame state.
|
|
2089
4034
|
// @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check.
|
|
@@ -2160,13 +4105,13 @@ var Tile3D = class {
|
|
|
2160
4105
|
this.lodMetricValue = this._unscaledLodMetricValue;
|
|
2161
4106
|
}
|
|
2162
4107
|
_initializeTransforms(tileHeader) {
|
|
2163
|
-
this.transform = tileHeader.transform ? new
|
|
4108
|
+
this.transform = tileHeader.transform ? new import_core8.Matrix4(tileHeader.transform) : new import_core8.Matrix4();
|
|
2164
4109
|
const parent = this.parent;
|
|
2165
4110
|
const tileset = this.tileset;
|
|
2166
4111
|
const parentTransform = parent && parent.computedTransform ? parent.computedTransform.clone() : tileset.modelMatrix.clone();
|
|
2167
|
-
this.computedTransform = new
|
|
2168
|
-
const parentInitialTransform = parent && parent._initialTransform ? parent._initialTransform.clone() : new
|
|
2169
|
-
this._initialTransform = new
|
|
4112
|
+
this.computedTransform = new import_core8.Matrix4(parentTransform).multiplyRight(this.transform);
|
|
4113
|
+
const parentInitialTransform = parent && parent._initialTransform ? parent._initialTransform.clone() : new import_core8.Matrix4();
|
|
4114
|
+
this._initialTransform = new import_core8.Matrix4(parentInitialTransform).multiplyRight(this.transform);
|
|
2170
4115
|
}
|
|
2171
4116
|
/**
|
|
2172
4117
|
* Updates the world-space LOD metric from the source metric and the complete tile transform.
|
|
@@ -2210,7 +4155,11 @@ var Tile3D = class {
|
|
|
2210
4155
|
this._distanceToCamera = 0;
|
|
2211
4156
|
this._centerZDepth = 0;
|
|
2212
4157
|
this._screenSpaceError = 0;
|
|
2213
|
-
this.
|
|
4158
|
+
this._screenSpaceErrorProgressiveResolution = 0;
|
|
4159
|
+
this._priorityProgressiveResolution = false;
|
|
4160
|
+
this._foveatedFactor = 0;
|
|
4161
|
+
this.priorityDeferred = false;
|
|
4162
|
+
this._visibilityPlaneMask = import_culling4.CullingVolume.MASK_INDETERMINATE;
|
|
2214
4163
|
this._visible = void 0;
|
|
2215
4164
|
this._inRequestVolume = false;
|
|
2216
4165
|
this._stackLength = 0;
|
|
@@ -2225,8 +4174,16 @@ var Tile3D = class {
|
|
|
2225
4174
|
_getRefine(refine) {
|
|
2226
4175
|
return refine || this.parent && this.parent.refine || TILE_REFINEMENT.REPLACE;
|
|
2227
4176
|
}
|
|
4177
|
+
/**
|
|
4178
|
+
* Returns whether loaded content is an external tileset.
|
|
4179
|
+
*
|
|
4180
|
+
* Parsed shape is authoritative because valid external resources may be extensionless, signed,
|
|
4181
|
+
* or use a misleading filename. This method is called only after source loading has populated
|
|
4182
|
+
* `content`.
|
|
4183
|
+
*/
|
|
2228
4184
|
_isTileset() {
|
|
2229
|
-
|
|
4185
|
+
var _a;
|
|
4186
|
+
return ((_a = this.content) == null ? void 0 : _a.shape) === "tileset3d";
|
|
2230
4187
|
}
|
|
2231
4188
|
_onContentLoaded() {
|
|
2232
4189
|
switch (this.content && this.content.type) {
|
|
@@ -2244,6 +4201,9 @@ var Tile3D = class {
|
|
|
2244
4201
|
}
|
|
2245
4202
|
_updateBoundingVolume(header) {
|
|
2246
4203
|
this.boundingVolume = createBoundingVolume(header.boundingVolume, this.computedTransform, this.boundingVolume);
|
|
4204
|
+
if (header.viewerRequestVolume) {
|
|
4205
|
+
this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
|
|
4206
|
+
}
|
|
2247
4207
|
const content = header.content;
|
|
2248
4208
|
if (!content) {
|
|
2249
4209
|
return;
|
|
@@ -2251,15 +4211,12 @@ var Tile3D = class {
|
|
|
2251
4211
|
if (content.boundingVolume) {
|
|
2252
4212
|
this._contentBoundingVolume = createBoundingVolume(content.boundingVolume, this.computedTransform, this._contentBoundingVolume);
|
|
2253
4213
|
}
|
|
2254
|
-
if (header.viewerRequestVolume) {
|
|
2255
|
-
this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
|
|
2256
|
-
}
|
|
2257
4214
|
}
|
|
2258
4215
|
/**
|
|
2259
4216
|
* Updates the tile's computed transform, world-space geometric error, and bounding volumes.
|
|
2260
4217
|
* @param parentTransform - Current computed transform of the parent tile or tileset.
|
|
2261
4218
|
*/
|
|
2262
|
-
_updateTransform(parentTransform = new
|
|
4219
|
+
_updateTransform(parentTransform = new import_core8.Matrix4()) {
|
|
2263
4220
|
const computedTransform = parentTransform.clone().multiplyRight(this.transform);
|
|
2264
4221
|
const didTransformChange = !computedTransform.equals(this.computedTransform);
|
|
2265
4222
|
if (!didTransformChange) {
|
|
@@ -2272,15 +4229,77 @@ var Tile3D = class {
|
|
|
2272
4229
|
};
|
|
2273
4230
|
|
|
2274
4231
|
// dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
|
|
2275
|
-
var import_loader_utils6 = require("@loaders.gl/loader-utils");
|
|
2276
4232
|
var import_geospatial7 = require("@math.gl/geospatial");
|
|
2277
|
-
var
|
|
4233
|
+
var import_core10 = require("@math.gl/core");
|
|
2278
4234
|
|
|
2279
4235
|
// dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js
|
|
2280
4236
|
var Tileset3DTraverser = class extends TilesetTraverser {
|
|
4237
|
+
/** Pending implicit subtree requests keyed by viewport and tile identity. */
|
|
4238
|
+
pendingImplicitSubtrees = /* @__PURE__ */ new Map();
|
|
4239
|
+
/**
|
|
4240
|
+
* Reports whether every implicit subtree request started by this viewport traversal has settled.
|
|
4241
|
+
*
|
|
4242
|
+
* @param frameState - Traversal frame whose completion callback is being considered.
|
|
4243
|
+
* @returns `true` when no matching subtree metadata request remains pending.
|
|
4244
|
+
*/
|
|
4245
|
+
traversalFinished(frameState) {
|
|
4246
|
+
const viewportPrefix = `${frameState.viewport.id}:`;
|
|
4247
|
+
for (const [requestKey, frameNumber] of this.pendingImplicitSubtrees) {
|
|
4248
|
+
if (requestKey.startsWith(viewportPrefix) && frameNumber === frameState.frameNumber) {
|
|
4249
|
+
return false;
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
return true;
|
|
4253
|
+
}
|
|
2281
4254
|
compareDistanceToCamera(a, b) {
|
|
2282
4255
|
return b._distanceToCamera === 0 && a._distanceToCamera === 0 ? b._centerZDepth - a._centerZDepth : b._distanceToCamera - a._distanceToCamera;
|
|
2283
4256
|
}
|
|
4257
|
+
/**
|
|
4258
|
+
* Starts an implicit subtree request only after culling, request-volume, and SSE eligibility.
|
|
4259
|
+
*
|
|
4260
|
+
* The current parent remains the traversal boundary while metadata is loading, which preserves
|
|
4261
|
+
* REPLACE coverage and ADD accumulation. Request scheduling reads the tile's existing
|
|
4262
|
+
* progressive-resolution and foveated metrics, so subtree metadata follows the same priority
|
|
4263
|
+
* policy as render content. A completion notification lets the owning tileset publish the newly
|
|
4264
|
+
* materialized traversal result on the next update.
|
|
4265
|
+
*
|
|
4266
|
+
* @param tile - Tile whose existing or lazy children should be updated.
|
|
4267
|
+
* @param frameState - Current culling and LOD state.
|
|
4268
|
+
*/
|
|
4269
|
+
updateChildTiles(tile, frameState) {
|
|
4270
|
+
if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && this.shouldRefine(tile, frameState)) {
|
|
4271
|
+
const requestKey = `${frameState.viewport.id}:${tile.id}`;
|
|
4272
|
+
if (!this.pendingImplicitSubtrees.has(requestKey)) {
|
|
4273
|
+
this.pendingImplicitSubtrees.set(requestKey, frameState.frameNumber);
|
|
4274
|
+
void tile.tileset._loadTileChildren(tile, frameState).finally(() => {
|
|
4275
|
+
this.handleImplicitSubtreeLoad(tile, frameState, requestKey);
|
|
4276
|
+
});
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
super.updateChildTiles(tile, frameState);
|
|
4280
|
+
}
|
|
4281
|
+
/**
|
|
4282
|
+
* Resumes the same traversal frame after one lazy subtree request settles.
|
|
4283
|
+
*
|
|
4284
|
+
* Successful materialization traverses the new headers immediately. Failed or scheduler-cancelled
|
|
4285
|
+
* requests finish the current traversal without retrying in a tight loop; a later application
|
|
4286
|
+
* update may retry them.
|
|
4287
|
+
*
|
|
4288
|
+
* @param tile - Subtree-root tile whose request settled.
|
|
4289
|
+
* @param frameState - Frame that started the request.
|
|
4290
|
+
* @param requestKey - Viewport-scoped pending-request identity.
|
|
4291
|
+
*/
|
|
4292
|
+
handleImplicitSubtreeLoad(tile, frameState, requestKey) {
|
|
4293
|
+
this.pendingImplicitSubtrees.delete(requestKey);
|
|
4294
|
+
if (this._frameNumber !== frameState.frameNumber) {
|
|
4295
|
+
return;
|
|
4296
|
+
}
|
|
4297
|
+
if (tile.childrenState === "ready") {
|
|
4298
|
+
this.executeTraversal(tile, frameState);
|
|
4299
|
+
} else if (this.traversalFinished(frameState)) {
|
|
4300
|
+
this.options.onTraversalEnd(frameState);
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
2284
4303
|
updateTileVisibility(tile, frameState) {
|
|
2285
4304
|
super.updateTileVisibility(tile, frameState);
|
|
2286
4305
|
if (!tile.isVisibleAndInRequestVolume) {
|
|
@@ -2316,19 +4335,19 @@ var Tileset3DTraverser = class extends TilesetTraverser {
|
|
|
2316
4335
|
};
|
|
2317
4336
|
|
|
2318
4337
|
// dist/tileset-3d/helpers/zoom.js
|
|
2319
|
-
var
|
|
2320
|
-
var
|
|
4338
|
+
var import_core9 = require("@math.gl/core");
|
|
4339
|
+
var import_culling5 = require("@math.gl/culling");
|
|
2321
4340
|
var import_geospatial6 = require("@math.gl/geospatial");
|
|
2322
4341
|
var WGS84_RADIUS_X = 6378137;
|
|
2323
4342
|
var WGS84_RADIUS_Y = 6378137;
|
|
2324
4343
|
var WGS84_RADIUS_Z = 6356752314245179e-9;
|
|
2325
|
-
var scratchVector3 = new
|
|
4344
|
+
var scratchVector3 = new import_core9.Vector3();
|
|
2326
4345
|
function getZoomFromBoundingVolume(boundingVolume, cartorgraphicCenter) {
|
|
2327
|
-
if (boundingVolume instanceof
|
|
4346
|
+
if (boundingVolume instanceof import_culling5.OrientedBoundingBox) {
|
|
2328
4347
|
const { halfAxes } = boundingVolume;
|
|
2329
4348
|
const obbSize = getObbSize(halfAxes);
|
|
2330
4349
|
return Math.log2(WGS84_RADIUS_Z / (obbSize + cartorgraphicCenter[2]));
|
|
2331
|
-
} else if (boundingVolume instanceof
|
|
4350
|
+
} else if (boundingVolume instanceof import_culling5.BoundingSphere) {
|
|
2332
4351
|
const { radius } = boundingVolume;
|
|
2333
4352
|
return Math.log2(WGS84_RADIUS_Z / (radius + cartorgraphicCenter[2]));
|
|
2334
4353
|
} else if (boundingVolume.width && boundingVolume.height) {
|
|
@@ -2357,6 +4376,220 @@ function getObbSize(halfAxes) {
|
|
|
2357
4376
|
return size;
|
|
2358
4377
|
}
|
|
2359
4378
|
|
|
4379
|
+
// dist/tileset-3d/format-3d-tiles/implicit-tiling.js
|
|
4380
|
+
var QUADTREE_CHILD_COUNT = 4;
|
|
4381
|
+
var OCTREE_CHILD_COUNT = 8;
|
|
4382
|
+
function createImplicitSubtreeReference(descriptor, coordinates) {
|
|
4383
|
+
return {
|
|
4384
|
+
coordinates,
|
|
4385
|
+
subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
|
|
4386
|
+
descriptor
|
|
4387
|
+
};
|
|
4388
|
+
}
|
|
4389
|
+
function materializeImplicitSubtree(subtree, reference) {
|
|
4390
|
+
const { descriptor } = reference;
|
|
4391
|
+
validateImplicitDescriptor(descriptor);
|
|
4392
|
+
const counters = { tileCount: 0, childSubtreeCount: 0 };
|
|
4393
|
+
const root = materializeAvailableTile(subtree, reference, { localLevel: 0, mortonIndex: 0 }, reference.coordinates, counters);
|
|
4394
|
+
if (!root) {
|
|
4395
|
+
throw new Error(`Invalid 3D Tiles subtree: root tile is unavailable at ${reference.subtreeUrl}`);
|
|
4396
|
+
}
|
|
4397
|
+
return { root, ...counters };
|
|
4398
|
+
}
|
|
4399
|
+
function replaceImplicitUrlTemplate(templateUrl, coordinates) {
|
|
4400
|
+
const values = {
|
|
4401
|
+
level: coordinates.level,
|
|
4402
|
+
x: coordinates.x,
|
|
4403
|
+
y: coordinates.y,
|
|
4404
|
+
z: coordinates.z
|
|
4405
|
+
};
|
|
4406
|
+
return templateUrl.replace(/{(level|x|y|z)}/gi, (_match, coordinateName) => String(values[coordinateName.toLowerCase()]));
|
|
4407
|
+
}
|
|
4408
|
+
function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
|
|
4409
|
+
const { descriptor } = reference;
|
|
4410
|
+
const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
|
|
4411
|
+
const availabilityIndex = getLevelOffset(childCount, localCoordinates.localLevel) + localCoordinates.mortonIndex;
|
|
4412
|
+
if (!getAvailabilityValue(subtree.tileAvailability, availabilityIndex)) {
|
|
4413
|
+
return null;
|
|
4414
|
+
}
|
|
4415
|
+
counters.tileCount++;
|
|
4416
|
+
const contentAvailable = getAvailabilityValue(getPrimaryContentAvailability(subtree.contentAvailability), availabilityIndex);
|
|
4417
|
+
const children = [];
|
|
4418
|
+
if (globalCoordinates.level < descriptor.maximumLevel) {
|
|
4419
|
+
if (localCoordinates.localLevel + 1 < descriptor.subtreeLevels) {
|
|
4420
|
+
for (let childIndex = 0; childIndex < childCount; childIndex++) {
|
|
4421
|
+
const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
|
|
4422
|
+
const childHeader = materializeAvailableTile(subtree, reference, {
|
|
4423
|
+
localLevel: localCoordinates.localLevel + 1,
|
|
4424
|
+
mortonIndex: localCoordinates.mortonIndex * childCount + childIndex
|
|
4425
|
+
}, childCoordinates, counters);
|
|
4426
|
+
if (childHeader) {
|
|
4427
|
+
children.push(childHeader);
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4430
|
+
} else {
|
|
4431
|
+
for (let childIndex = 0; childIndex < childCount; childIndex++) {
|
|
4432
|
+
const childSubtreeIndex = localCoordinates.mortonIndex * childCount + childIndex;
|
|
4433
|
+
if (getAvailabilityValue(subtree.childSubtreeAvailability, childSubtreeIndex)) {
|
|
4434
|
+
const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
|
|
4435
|
+
children.push(createLazyImplicitTileHeader(descriptor, childCoordinates));
|
|
4436
|
+
counters.childSubtreeCount++;
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
return formatImplicitTileHeader(descriptor, reference, globalCoordinates, contentAvailable, children);
|
|
4442
|
+
}
|
|
4443
|
+
function createLazyImplicitTileHeader(descriptor, coordinates) {
|
|
4444
|
+
const reference = createImplicitSubtreeReference(descriptor, coordinates);
|
|
4445
|
+
return {
|
|
4446
|
+
id: getImplicitTileId(reference, coordinates),
|
|
4447
|
+
children: [],
|
|
4448
|
+
implicitSubtree: reference,
|
|
4449
|
+
boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
|
|
4450
|
+
geometricError: descriptor.rootLodMetricValue / 2 ** coordinates.level,
|
|
4451
|
+
lodMetricType: descriptor.lodMetricType,
|
|
4452
|
+
lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
|
|
4453
|
+
refine: descriptor.refine,
|
|
4454
|
+
type: TILE_TYPE.EMPTY
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
function formatImplicitTileHeader(descriptor, reference, coordinates, contentAvailable, children) {
|
|
4458
|
+
const contentUrl = contentAvailable && descriptor.contentUrlTemplate ? replaceImplicitUrlTemplate(descriptor.contentUrlTemplate, coordinates) : void 0;
|
|
4459
|
+
const lodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
|
|
4460
|
+
return {
|
|
4461
|
+
id: getImplicitTileId(reference, coordinates),
|
|
4462
|
+
children,
|
|
4463
|
+
contentUrl,
|
|
4464
|
+
content: contentUrl ? { ...descriptor.contentHeader, uri: contentUrl } : void 0,
|
|
4465
|
+
refine: descriptor.refine,
|
|
4466
|
+
type: getImplicitTileType(contentUrl),
|
|
4467
|
+
lodMetricType: descriptor.lodMetricType,
|
|
4468
|
+
lodMetricValue,
|
|
4469
|
+
geometricError: lodMetricValue,
|
|
4470
|
+
boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme)
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
function getImplicitTileId(reference, coordinates) {
|
|
4474
|
+
return `${reference.subtreeUrl}#implicit=${coordinates.level}/${coordinates.x}/${coordinates.y}/${coordinates.z}`;
|
|
4475
|
+
}
|
|
4476
|
+
function getAvailabilityValue(availability, index) {
|
|
4477
|
+
if (typeof (availability == null ? void 0 : availability.constant) === "number") {
|
|
4478
|
+
return Boolean(availability.constant);
|
|
4479
|
+
}
|
|
4480
|
+
const bitstream = availability == null ? void 0 : availability.explicitBitstream;
|
|
4481
|
+
if (!bitstream) {
|
|
4482
|
+
return false;
|
|
4483
|
+
}
|
|
4484
|
+
const byteIndex = Math.floor(index / 8);
|
|
4485
|
+
const bitIndex = index % 8;
|
|
4486
|
+
return Boolean(bitstream[byteIndex] >> bitIndex & 1);
|
|
4487
|
+
}
|
|
4488
|
+
function getPrimaryContentAvailability(availability) {
|
|
4489
|
+
return Array.isArray(availability) ? availability[0] || { constant: 0 } : availability || { constant: 0 };
|
|
4490
|
+
}
|
|
4491
|
+
function getLevelOffset(childCount, localLevel) {
|
|
4492
|
+
return (childCount ** localLevel - 1) / (childCount - 1);
|
|
4493
|
+
}
|
|
4494
|
+
function getChildCoordinates(parent, childIndex, subdivisionScheme) {
|
|
4495
|
+
return {
|
|
4496
|
+
level: parent.level + 1,
|
|
4497
|
+
x: parent.x * 2 + (childIndex & 1),
|
|
4498
|
+
y: parent.y * 2 + (childIndex >> 1 & 1),
|
|
4499
|
+
z: subdivisionScheme === "OCTREE" ? parent.z * 2 + (childIndex >> 2 & 1) : 0
|
|
4500
|
+
};
|
|
4501
|
+
}
|
|
4502
|
+
function getImplicitChildCount(subdivisionScheme) {
|
|
4503
|
+
return subdivisionScheme === "OCTREE" ? OCTREE_CHILD_COUNT : QUADTREE_CHILD_COUNT;
|
|
4504
|
+
}
|
|
4505
|
+
function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
|
|
4506
|
+
if (rootBoundingVolume.region) {
|
|
4507
|
+
const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
|
|
4508
|
+
const divisionCount = 2 ** coordinates.level;
|
|
4509
|
+
const longitudeSize = (east - west) / divisionCount;
|
|
4510
|
+
const latitudeSize = (north - south) / divisionCount;
|
|
4511
|
+
const childWest = west + longitudeSize * coordinates.x;
|
|
4512
|
+
const childSouth = south + latitudeSize * coordinates.y;
|
|
4513
|
+
let childMinimumHeight = minimumHeight;
|
|
4514
|
+
let childMaximumHeight = maximumHeight;
|
|
4515
|
+
if (subdivisionScheme === "OCTREE") {
|
|
4516
|
+
const heightSize = (maximumHeight - minimumHeight) / divisionCount;
|
|
4517
|
+
childMinimumHeight = minimumHeight + heightSize * coordinates.z;
|
|
4518
|
+
childMaximumHeight = childMinimumHeight + heightSize;
|
|
4519
|
+
}
|
|
4520
|
+
return {
|
|
4521
|
+
region: [
|
|
4522
|
+
childWest,
|
|
4523
|
+
childSouth,
|
|
4524
|
+
childWest + longitudeSize,
|
|
4525
|
+
childSouth + latitudeSize,
|
|
4526
|
+
childMinimumHeight,
|
|
4527
|
+
childMaximumHeight
|
|
4528
|
+
]
|
|
4529
|
+
};
|
|
4530
|
+
}
|
|
4531
|
+
if (rootBoundingVolume.box) {
|
|
4532
|
+
if (rootBoundingVolume.s2VolumeInfo) {
|
|
4533
|
+
return { box: [...rootBoundingVolume.box], s2VolumeInfo: rootBoundingVolume.s2VolumeInfo };
|
|
4534
|
+
}
|
|
4535
|
+
const box = rootBoundingVolume.box;
|
|
4536
|
+
const divisionCount = 2 ** coordinates.level;
|
|
4537
|
+
const xOffset = -1 + (2 * coordinates.x + 1) / divisionCount;
|
|
4538
|
+
const yOffset = -1 + (2 * coordinates.y + 1) / divisionCount;
|
|
4539
|
+
const zOffset = -1 + (2 * coordinates.z + 1) / divisionCount;
|
|
4540
|
+
const divideHeight = subdivisionScheme === "OCTREE";
|
|
4541
|
+
const center = [
|
|
4542
|
+
box[0] + box[3] * xOffset + box[6] * yOffset + (divideHeight ? box[9] * zOffset : 0),
|
|
4543
|
+
box[1] + box[4] * xOffset + box[7] * yOffset + (divideHeight ? box[10] * zOffset : 0),
|
|
4544
|
+
box[2] + box[5] * xOffset + box[8] * yOffset + (divideHeight ? box[11] * zOffset : 0)
|
|
4545
|
+
];
|
|
4546
|
+
return {
|
|
4547
|
+
box: [
|
|
4548
|
+
...center,
|
|
4549
|
+
box[3] / divisionCount,
|
|
4550
|
+
box[4] / divisionCount,
|
|
4551
|
+
box[5] / divisionCount,
|
|
4552
|
+
box[6] / divisionCount,
|
|
4553
|
+
box[7] / divisionCount,
|
|
4554
|
+
box[8] / divisionCount,
|
|
4555
|
+
divideHeight ? box[9] / divisionCount : box[9],
|
|
4556
|
+
divideHeight ? box[10] / divisionCount : box[10],
|
|
4557
|
+
divideHeight ? box[11] / divisionCount : box[11]
|
|
4558
|
+
]
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
throw new Error(`Unsupported implicit 3D Tiles bounding volume: ${JSON.stringify(rootBoundingVolume)}`);
|
|
4562
|
+
}
|
|
4563
|
+
function getImplicitTileType(contentUrl) {
|
|
4564
|
+
var _a;
|
|
4565
|
+
if (!contentUrl) {
|
|
4566
|
+
return TILE_TYPE.EMPTY;
|
|
4567
|
+
}
|
|
4568
|
+
const extension = (_a = contentUrl.split("?")[0].split(".").pop()) == null ? void 0 : _a.toLowerCase();
|
|
4569
|
+
switch (extension) {
|
|
4570
|
+
case "pnts":
|
|
4571
|
+
return TILE_TYPE.POINTCLOUD;
|
|
4572
|
+
case "i3dm":
|
|
4573
|
+
case "b3dm":
|
|
4574
|
+
case "glb":
|
|
4575
|
+
case "gltf":
|
|
4576
|
+
return TILE_TYPE.SCENEGRAPH;
|
|
4577
|
+
default:
|
|
4578
|
+
return extension || TILE_TYPE.EMPTY;
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
function validateImplicitDescriptor(descriptor) {
|
|
4582
|
+
if (descriptor.subdivisionScheme !== "QUADTREE" && descriptor.subdivisionScheme !== "OCTREE") {
|
|
4583
|
+
throw new Error(`Unsupported implicit subdivision scheme: ${descriptor.subdivisionScheme}`);
|
|
4584
|
+
}
|
|
4585
|
+
if (!Number.isInteger(descriptor.subtreeLevels) || descriptor.subtreeLevels < 1) {
|
|
4586
|
+
throw new Error("Implicit subtreeLevels must be a positive integer");
|
|
4587
|
+
}
|
|
4588
|
+
if (!Number.isInteger(descriptor.maximumLevel) || descriptor.maximumLevel < 0) {
|
|
4589
|
+
throw new Error("Implicit availableLevels must describe at least the root level");
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
|
|
2360
4593
|
// dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
|
|
2361
4594
|
var EMPTY_CONTENT_FORMATS = {
|
|
2362
4595
|
draco: false,
|
|
@@ -2364,6 +4597,7 @@ var EMPTY_CONTENT_FORMATS = {
|
|
|
2364
4597
|
dds: false,
|
|
2365
4598
|
ktx2: false
|
|
2366
4599
|
};
|
|
4600
|
+
var DEFAULT_MAXIMUM_CACHED_SUBTREES = 32;
|
|
2367
4601
|
var Tiles3DSource = class {
|
|
2368
4602
|
/** 3D Tiles format discriminator. */
|
|
2369
4603
|
type = TILESET_TYPE.TILES3D;
|
|
@@ -2392,6 +4626,23 @@ var Tiles3DSource = class {
|
|
|
2392
4626
|
/** Normalized source metadata after initialization. */
|
|
2393
4627
|
metadata;
|
|
2394
4628
|
queryParams = {};
|
|
4629
|
+
/** Final request URLs cached by unmodified tile content URL. */
|
|
4630
|
+
tileUrlCache = /* @__PURE__ */ new Map();
|
|
4631
|
+
/** Parsed subtree promises keyed by final source URL for request deduplication and LRU reuse. */
|
|
4632
|
+
implicitSubtreeCache = /* @__PURE__ */ new Map();
|
|
4633
|
+
/** URLs whose subtree resource requests have not settled. */
|
|
4634
|
+
pendingImplicitSubtreeUrls = /* @__PURE__ */ new Set();
|
|
4635
|
+
/** Maximum number of settled parsed subtrees retained by this source. */
|
|
4636
|
+
maximumCachedSubtrees;
|
|
4637
|
+
/** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
|
|
4638
|
+
implicitTilingStats = {
|
|
4639
|
+
requestedSubtrees: 0,
|
|
4640
|
+
loadedSubtrees: 0,
|
|
4641
|
+
cacheHits: 0,
|
|
4642
|
+
materializedTiles: 0
|
|
4643
|
+
};
|
|
4644
|
+
/** Whether the owning tileset has released this source. */
|
|
4645
|
+
destroyed = false;
|
|
2395
4646
|
extensionsUsed = [];
|
|
2396
4647
|
resolver;
|
|
2397
4648
|
rootTileset;
|
|
@@ -2401,15 +4652,30 @@ var Tiles3DSource = class {
|
|
|
2401
4652
|
* @param loadOptions Loader options forwarded to tile requests.
|
|
2402
4653
|
*/
|
|
2403
4654
|
constructor(input, loadOptions = {}) {
|
|
4655
|
+
var _a;
|
|
2404
4656
|
const request = normalizeTiles3DRequest(input);
|
|
2405
4657
|
this.rootTileset = isTilesetRequest(input) ? null : input;
|
|
2406
4658
|
this.tileset = this.rootTileset;
|
|
2407
4659
|
this.loader = request.loader;
|
|
2408
4660
|
this.url = request.url;
|
|
2409
|
-
this.basePath = request.basePath ||
|
|
4661
|
+
this.basePath = request.basePath || path_exports.dirname(request.url);
|
|
2410
4662
|
this.resolver = request.resolver;
|
|
2411
4663
|
this.coreApi = request.coreApi;
|
|
2412
4664
|
this.loadOptions = loadOptions;
|
|
4665
|
+
const maximumCachedSubtrees = Number((_a = loadOptions["3d-tiles"]) == null ? void 0 : _a.maximumCachedSubtrees);
|
|
4666
|
+
this.maximumCachedSubtrees = Number.isFinite(maximumCachedSubtrees) ? Math.max(0, Math.floor(maximumCachedSubtrees)) : DEFAULT_MAXIMUM_CACHED_SUBTREES;
|
|
4667
|
+
}
|
|
4668
|
+
/**
|
|
4669
|
+
* Releases URL and parsed-subtree caches and blocks late subtree installation.
|
|
4670
|
+
*
|
|
4671
|
+
* The injected resolver or core API remains responsible for transport-level abort signals. A
|
|
4672
|
+
* request that cannot be aborted may finish parsing, but it will not mutate destroyed tiles.
|
|
4673
|
+
*/
|
|
4674
|
+
destroy() {
|
|
4675
|
+
this.destroyed = true;
|
|
4676
|
+
this.tileUrlCache.clear();
|
|
4677
|
+
this.implicitSubtreeCache.clear();
|
|
4678
|
+
this.pendingImplicitSubtreeUrls.clear();
|
|
2413
4679
|
}
|
|
2414
4680
|
/**
|
|
2415
4681
|
* Fetches and validates top-level metadata, then normalizes common fields used by {@link Tileset3D}.
|
|
@@ -2429,7 +4695,9 @@ var Tiles3DSource = class {
|
|
|
2429
4695
|
this.tileset = this.rootTileset;
|
|
2430
4696
|
if (this.rootTileset.queryString) {
|
|
2431
4697
|
const searchParams = new URLSearchParams(this.rootTileset.queryString);
|
|
2432
|
-
|
|
4698
|
+
for (const [parameterName, parameterValue] of searchParams.entries()) {
|
|
4699
|
+
this.setQueryParameter(parameterName, parameterValue);
|
|
4700
|
+
}
|
|
2433
4701
|
}
|
|
2434
4702
|
this.asset = this.rootTileset.asset;
|
|
2435
4703
|
if (!this.asset) {
|
|
@@ -2439,7 +4707,7 @@ var Tiles3DSource = class {
|
|
|
2439
4707
|
throw new Error("The tileset must be 3D Tiles version either 0.0 or 1.0 or 1.1.");
|
|
2440
4708
|
}
|
|
2441
4709
|
if ("tilesetVersion" in this.asset) {
|
|
2442
|
-
this.
|
|
4710
|
+
this.setQueryParameter("v", this.asset.tilesetVersion);
|
|
2443
4711
|
}
|
|
2444
4712
|
this.properties = this.rootTileset.properties;
|
|
2445
4713
|
this.extras = this.rootTileset.extras;
|
|
@@ -2472,7 +4740,7 @@ var Tiles3DSource = class {
|
|
|
2472
4740
|
return this.getMetadata().tileset;
|
|
2473
4741
|
}
|
|
2474
4742
|
/**
|
|
2475
|
-
* Builds
|
|
4743
|
+
* Builds explicit runtime headers while leaving implicit subtree references lazy.
|
|
2476
4744
|
*/
|
|
2477
4745
|
initializeTileHeaders(tileset, tilesetJson, parentTile) {
|
|
2478
4746
|
var _a;
|
|
@@ -2492,7 +4760,7 @@ var Tiles3DSource = class {
|
|
|
2492
4760
|
const url = new URL(childTile.contentUrl);
|
|
2493
4761
|
const session = url.searchParams.get("session");
|
|
2494
4762
|
if (session) {
|
|
2495
|
-
this.
|
|
4763
|
+
this.setQueryParameter("session", session);
|
|
2496
4764
|
}
|
|
2497
4765
|
}
|
|
2498
4766
|
tile.children.push(childTile);
|
|
@@ -2518,7 +4786,9 @@ var Tiles3DSource = class {
|
|
|
2518
4786
|
...this.loadOptions,
|
|
2519
4787
|
[this.loader.id]: {
|
|
2520
4788
|
...tilesetLoaderOptions,
|
|
2521
|
-
|
|
4789
|
+
// Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
|
|
4790
|
+
// payloads. This is required for signed and extensionless resources.
|
|
4791
|
+
isTileset: "auto",
|
|
2522
4792
|
assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y"
|
|
2523
4793
|
}
|
|
2524
4794
|
};
|
|
@@ -2526,17 +4796,77 @@ var Tiles3DSource = class {
|
|
|
2526
4796
|
tile.content = content;
|
|
2527
4797
|
return {
|
|
2528
4798
|
loaded: true,
|
|
2529
|
-
nestedTileset:
|
|
4799
|
+
nestedTileset: (content == null ? void 0 : content.shape) === "tileset3d" ? content : void 0
|
|
4800
|
+
};
|
|
4801
|
+
}
|
|
4802
|
+
/**
|
|
4803
|
+
* Loads, materializes, and installs exactly one implicit subtree.
|
|
4804
|
+
*
|
|
4805
|
+
* Final URLs pass through the same query inheritance and archive/custom resolver path as render
|
|
4806
|
+
* content. Parsed resources are deduplicated by final URL, while the pure materializer creates
|
|
4807
|
+
* lazy placeholders for every available child subtree instead of recursively fetching them.
|
|
4808
|
+
*
|
|
4809
|
+
* @param tile - Existing contentless subtree-root placeholder.
|
|
4810
|
+
* @param frameState - View state that made this request eligible; priority is consumed earlier.
|
|
4811
|
+
* @returns Counts describing the installed subtree.
|
|
4812
|
+
*/
|
|
4813
|
+
async loadTileChildren(tile, frameState) {
|
|
4814
|
+
void frameState;
|
|
4815
|
+
const reference = tile.header.implicitSubtree;
|
|
4816
|
+
if (!reference) {
|
|
4817
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
4818
|
+
}
|
|
4819
|
+
const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
|
|
4820
|
+
const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
|
|
4821
|
+
if (this.destroyed || tile.isDestroyed()) {
|
|
4822
|
+
return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
|
|
4823
|
+
}
|
|
4824
|
+
const materializedSubtree = materializeImplicitSubtree(subtree, {
|
|
4825
|
+
...reference,
|
|
4826
|
+
subtreeUrl
|
|
4827
|
+
});
|
|
4828
|
+
tile.applyImplicitSubtreeHeader(materializedSubtree.root);
|
|
4829
|
+
const materializedTileCount = this.initializeMaterializedChildren(tile.tileset, tile, materializedSubtree.root.children);
|
|
4830
|
+
this.implicitTilingStats.loadedSubtrees++;
|
|
4831
|
+
this.implicitTilingStats.materializedTiles += materializedTileCount;
|
|
4832
|
+
return {
|
|
4833
|
+
loaded: true,
|
|
4834
|
+
tileCount: materializedSubtree.tileCount,
|
|
4835
|
+
childSubtreeCount: materializedSubtree.childSubtreeCount
|
|
4836
|
+
};
|
|
4837
|
+
}
|
|
4838
|
+
/**
|
|
4839
|
+
* Returns a snapshot of implicit-subtree request, cache, and materialization counters.
|
|
4840
|
+
*
|
|
4841
|
+
* @returns Immutable-by-convention diagnostic values for runtime inspection.
|
|
4842
|
+
*/
|
|
4843
|
+
getImplicitTilingStats() {
|
|
4844
|
+
return {
|
|
4845
|
+
...this.implicitTilingStats,
|
|
4846
|
+
cachedSubtrees: this.implicitSubtreeCache.size - this.pendingImplicitSubtreeUrls.size,
|
|
4847
|
+
pendingSubtrees: this.pendingImplicitSubtreeUrls.size
|
|
2530
4848
|
};
|
|
2531
4849
|
}
|
|
2532
4850
|
/**
|
|
2533
4851
|
* Resolves a tile content URL with source-managed query parameters.
|
|
4852
|
+
*
|
|
4853
|
+
* Existing per-resource parameters take precedence over inherited root, version, and session
|
|
4854
|
+
* values. Completed URLs are cached by the original tile path; {@link setQueryParameter}
|
|
4855
|
+
* invalidates the cache before changed source state can be observed.
|
|
4856
|
+
*
|
|
4857
|
+
* @param tilePath - Unmodified absolute content URL or data URL from the tile header.
|
|
4858
|
+
* @returns Content URL with any missing source parameters appended.
|
|
2534
4859
|
*/
|
|
2535
4860
|
getTileUrl(tilePath) {
|
|
2536
4861
|
if (tilePath.startsWith("data:")) {
|
|
2537
4862
|
return tilePath;
|
|
2538
4863
|
}
|
|
4864
|
+
const cachedTileUrl = this.tileUrlCache.get(tilePath);
|
|
4865
|
+
if (cachedTileUrl) {
|
|
4866
|
+
return cachedTileUrl;
|
|
4867
|
+
}
|
|
2539
4868
|
if (!Object.keys(this.queryParams).length) {
|
|
4869
|
+
this.tileUrlCache.set(tilePath, tilePath);
|
|
2540
4870
|
return tilePath;
|
|
2541
4871
|
}
|
|
2542
4872
|
const [pathWithoutQuery, existingQuery = ""] = tilePath.split("?");
|
|
@@ -2547,7 +4877,26 @@ var Tiles3DSource = class {
|
|
|
2547
4877
|
}
|
|
2548
4878
|
}
|
|
2549
4879
|
const queryParams = mergedQueryParams.toString();
|
|
2550
|
-
|
|
4880
|
+
const tileUrl = queryParams ? `${pathWithoutQuery}?${queryParams}` : pathWithoutQuery;
|
|
4881
|
+
this.tileUrlCache.set(tilePath, tileUrl);
|
|
4882
|
+
return tileUrl;
|
|
4883
|
+
}
|
|
4884
|
+
/**
|
|
4885
|
+
* Updates an inherited source query parameter and invalidates derived request URLs.
|
|
4886
|
+
*
|
|
4887
|
+
* Root tokens and tileset versions normally settle during initialization. Some providers expose
|
|
4888
|
+
* a session parameter on a child URL, so invalidation is required to prevent URLs cached earlier
|
|
4889
|
+
* in header construction from retaining stale authentication state.
|
|
4890
|
+
*
|
|
4891
|
+
* @param parameterName - Query parameter name.
|
|
4892
|
+
* @param parameterValue - Query parameter value.
|
|
4893
|
+
*/
|
|
4894
|
+
setQueryParameter(parameterName, parameterValue) {
|
|
4895
|
+
if (this.queryParams[parameterName] === parameterValue) {
|
|
4896
|
+
return;
|
|
4897
|
+
}
|
|
4898
|
+
this.queryParams[parameterName] = parameterValue;
|
|
4899
|
+
this.tileUrlCache.clear();
|
|
2551
4900
|
}
|
|
2552
4901
|
/**
|
|
2553
4902
|
* Derives the default view state from the root bounding volume.
|
|
@@ -2564,10 +4913,10 @@ var Tiles3DSource = class {
|
|
|
2564
4913
|
const { center } = rootTile.boundingVolume;
|
|
2565
4914
|
let cartographicCenter;
|
|
2566
4915
|
if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
|
|
2567
|
-
cartographicCenter = new
|
|
4916
|
+
cartographicCenter = new import_core10.Vector3();
|
|
2568
4917
|
import_geospatial7.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
|
|
2569
4918
|
} else {
|
|
2570
|
-
cartographicCenter = new
|
|
4919
|
+
cartographicCenter = new import_core10.Vector3(0, 0, -import_geospatial7.Ellipsoid.WGS84.radii[0]);
|
|
2571
4920
|
}
|
|
2572
4921
|
return {
|
|
2573
4922
|
asset: this.asset,
|
|
@@ -2608,11 +4957,11 @@ var Tiles3DSource = class {
|
|
|
2608
4957
|
/**
|
|
2609
4958
|
* Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
|
|
2610
4959
|
*/
|
|
2611
|
-
async loadWithCoreApi(url, options) {
|
|
4960
|
+
async loadWithCoreApi(url, options, loader = this.loader) {
|
|
2612
4961
|
if (!this.coreApi) {
|
|
2613
4962
|
throw new Error("Tiles3DSource requires an injected coreApi to load tileset data");
|
|
2614
4963
|
}
|
|
2615
|
-
return await this.coreApi.load(url,
|
|
4964
|
+
return await this.coreApi.load(url, loader, options);
|
|
2616
4965
|
}
|
|
2617
4966
|
/**
|
|
2618
4967
|
* Loads data through an injected resolver when present, otherwise through the injected core API.
|
|
@@ -2624,13 +4973,92 @@ var Tiles3DSource = class {
|
|
|
2624
4973
|
return await this.loadWithCoreApi(url, options);
|
|
2625
4974
|
}
|
|
2626
4975
|
/**
|
|
2627
|
-
* Loads
|
|
4976
|
+
* Loads an arbitrary source resource through an injected resolver or core API.
|
|
2628
4977
|
*/
|
|
2629
|
-
async loadResourceData(url, options) {
|
|
4978
|
+
async loadResourceData(url, options, loader = this.loader) {
|
|
2630
4979
|
if (this.resolver) {
|
|
2631
|
-
return await this.resolver.loadResource(url,
|
|
4980
|
+
return await this.resolver.loadResource(url, loader, options);
|
|
4981
|
+
}
|
|
4982
|
+
return await this.loadWithCoreApi(url, options, loader);
|
|
4983
|
+
}
|
|
4984
|
+
/**
|
|
4985
|
+
* Returns a parsed subtree from the LRU cache or starts one source-managed request.
|
|
4986
|
+
*
|
|
4987
|
+
* @param subtreeUrl - Final subtree URL after query inheritance.
|
|
4988
|
+
* @returns Parsed subtree availability data.
|
|
4989
|
+
*/
|
|
4990
|
+
async loadImplicitSubtreeResource(subtreeUrl) {
|
|
4991
|
+
const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
|
|
4992
|
+
if (cachedSubtree) {
|
|
4993
|
+
this.implicitTilingStats.cacheHits++;
|
|
4994
|
+
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
4995
|
+
this.implicitSubtreeCache.set(subtreeUrl, cachedSubtree);
|
|
4996
|
+
return await cachedSubtree;
|
|
4997
|
+
}
|
|
4998
|
+
this.implicitTilingStats.requestedSubtrees++;
|
|
4999
|
+
this.pendingImplicitSubtreeUrls.add(subtreeUrl);
|
|
5000
|
+
const loaderOptions = this.loadOptions[this.loader.id] || {};
|
|
5001
|
+
const subtreePromise = this.loadResourceData(subtreeUrl, {
|
|
5002
|
+
...this.loadOptions,
|
|
5003
|
+
[this.loader.id]: {
|
|
5004
|
+
...loaderOptions,
|
|
5005
|
+
isTileset: false,
|
|
5006
|
+
isSubtree: true
|
|
5007
|
+
}
|
|
5008
|
+
});
|
|
5009
|
+
this.implicitSubtreeCache.set(subtreeUrl, subtreePromise);
|
|
5010
|
+
try {
|
|
5011
|
+
return await subtreePromise;
|
|
5012
|
+
} catch (error) {
|
|
5013
|
+
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
5014
|
+
throw error;
|
|
5015
|
+
} finally {
|
|
5016
|
+
this.pendingImplicitSubtreeUrls.delete(subtreeUrl);
|
|
5017
|
+
this.trimImplicitSubtreeCache();
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
/**
|
|
5021
|
+
* Instantiates every header represented by one materialized subtree.
|
|
5022
|
+
*
|
|
5023
|
+
* @param tileset - Owning runtime tileset.
|
|
5024
|
+
* @param parentTile - Existing materialized parent.
|
|
5025
|
+
* @param childHeaders - Headers to install below the parent.
|
|
5026
|
+
* @returns Number of newly allocated runtime tile nodes.
|
|
5027
|
+
*/
|
|
5028
|
+
initializeMaterializedChildren(tileset, parentTile, childHeaders) {
|
|
5029
|
+
var _a;
|
|
5030
|
+
let materializedTileCount = 0;
|
|
5031
|
+
const stack = [
|
|
5032
|
+
{ parentTile, childHeaders }
|
|
5033
|
+
];
|
|
5034
|
+
while (stack.length > 0) {
|
|
5035
|
+
const entry = stack.pop();
|
|
5036
|
+
for (const childHeader of entry.childHeaders) {
|
|
5037
|
+
const childTile = new Tile3D(tileset, childHeader, entry.parentTile);
|
|
5038
|
+
entry.parentTile.children.push(childTile);
|
|
5039
|
+
childTile.depth = entry.parentTile.depth + 1;
|
|
5040
|
+
tileset.stats.get("Tiles In Tileset(s)").incrementCount();
|
|
5041
|
+
materializedTileCount++;
|
|
5042
|
+
if ((_a = childHeader.children) == null ? void 0 : _a.length) {
|
|
5043
|
+
stack.push({ parentTile: childTile, childHeaders: childHeader.children });
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
return materializedTileCount;
|
|
5048
|
+
}
|
|
5049
|
+
/** Evicts least-recently-used settled subtree entries until the configured bound is met. */
|
|
5050
|
+
trimImplicitSubtreeCache() {
|
|
5051
|
+
if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
|
|
5052
|
+
return;
|
|
5053
|
+
}
|
|
5054
|
+
for (const subtreeUrl of this.implicitSubtreeCache.keys()) {
|
|
5055
|
+
if (!this.pendingImplicitSubtreeUrls.has(subtreeUrl)) {
|
|
5056
|
+
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
5057
|
+
}
|
|
5058
|
+
if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
|
|
5059
|
+
break;
|
|
5060
|
+
}
|
|
2632
5061
|
}
|
|
2633
|
-
return await this.loadWithCoreApi(url, options);
|
|
2634
5062
|
}
|
|
2635
5063
|
};
|
|
2636
5064
|
function isTilesetRequest(input) {
|
|
@@ -2640,22 +5068,21 @@ function normalizeTiles3DRequest(input) {
|
|
|
2640
5068
|
if (isTilesetRequest(input)) {
|
|
2641
5069
|
return {
|
|
2642
5070
|
...input,
|
|
2643
|
-
basePath: input.basePath ||
|
|
5071
|
+
basePath: input.basePath || path_exports.dirname(input.url)
|
|
2644
5072
|
};
|
|
2645
5073
|
}
|
|
2646
5074
|
return {
|
|
2647
5075
|
url: input.url,
|
|
2648
5076
|
loader: input.loader,
|
|
2649
|
-
basePath: input.basePath ||
|
|
5077
|
+
basePath: input.basePath || path_exports.dirname(input.url),
|
|
2650
5078
|
resolver: input.resolver,
|
|
2651
5079
|
coreApi: input.coreApi
|
|
2652
5080
|
};
|
|
2653
5081
|
}
|
|
2654
5082
|
|
|
2655
5083
|
// dist/tileset-3d/format-i3s/i3s-source.js
|
|
2656
|
-
var import_loader_utils7 = require("@loaders.gl/loader-utils");
|
|
2657
5084
|
var import_geospatial8 = require("@math.gl/geospatial");
|
|
2658
|
-
var
|
|
5085
|
+
var import_core11 = require("@math.gl/core");
|
|
2659
5086
|
|
|
2660
5087
|
// dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js
|
|
2661
5088
|
var I3SPendingTilesRegister = class {
|
|
@@ -2829,7 +5256,7 @@ var I3STilesetTraverser = class extends TilesetTraverser {
|
|
|
2829
5256
|
tile.children.push(childTile);
|
|
2830
5257
|
const frameState = this._tileManager.find(childTile.id).frameState;
|
|
2831
5258
|
this.updateTile(childTile, frameState);
|
|
2832
|
-
if (this._frameNumber === frameState.frameNumber && (this.traversalFinished(frameState) || new Date().getTime() - this.lastUpdate > this.updateDebounceTime)) {
|
|
5259
|
+
if (this._frameNumber === frameState.frameNumber && (this.traversalFinished(frameState) || (/* @__PURE__ */ new Date()).getTime() - this.lastUpdate > this.updateDebounceTime)) {
|
|
2833
5260
|
this.executeTraversal(childTile, frameState);
|
|
2834
5261
|
}
|
|
2835
5262
|
}
|
|
@@ -2878,7 +5305,7 @@ var I3SSource = class {
|
|
|
2878
5305
|
this.tileset = this.rootTileset;
|
|
2879
5306
|
this.loader = request.loader;
|
|
2880
5307
|
this.url = request.url;
|
|
2881
|
-
this.basePath = request.basePath ||
|
|
5308
|
+
this.basePath = request.basePath || path_exports.dirname(request.url);
|
|
2882
5309
|
this.resolver = request.resolver;
|
|
2883
5310
|
this.coreApi = request.coreApi;
|
|
2884
5311
|
this.loadOptions = loadOptions;
|
|
@@ -3019,8 +5446,8 @@ var I3SSource = class {
|
|
|
3019
5446
|
const fullExtent = metadata.tileset.fullExtent;
|
|
3020
5447
|
if (fullExtent) {
|
|
3021
5448
|
const { xmin, xmax, ymin, ymax, zmin, zmax } = fullExtent;
|
|
3022
|
-
const cartographicCenter = new
|
|
3023
|
-
const cartesianCenter = new
|
|
5449
|
+
const cartographicCenter = new import_core11.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
|
|
5450
|
+
const cartesianCenter = new import_core11.Vector3();
|
|
3024
5451
|
import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
|
|
3025
5452
|
return {
|
|
3026
5453
|
cartographicCenter,
|
|
@@ -3031,8 +5458,8 @@ var I3SSource = class {
|
|
|
3031
5458
|
const extent = (_a = metadata.tileset.store) == null ? void 0 : _a.extent;
|
|
3032
5459
|
if (extent) {
|
|
3033
5460
|
const [xmin, ymin, xmax, ymax] = extent;
|
|
3034
|
-
const cartographicCenter = new
|
|
3035
|
-
const cartesianCenter = new
|
|
5461
|
+
const cartographicCenter = new import_core11.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
|
|
5462
|
+
const cartesianCenter = new import_core11.Vector3();
|
|
3036
5463
|
import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
|
|
3037
5464
|
return {
|
|
3038
5465
|
cartographicCenter,
|
|
@@ -3042,7 +5469,7 @@ var I3SSource = class {
|
|
|
3042
5469
|
}
|
|
3043
5470
|
console.warn("Extent is not defined in the tileset header");
|
|
3044
5471
|
return {
|
|
3045
|
-
cartographicCenter: new
|
|
5472
|
+
cartographicCenter: new import_core11.Vector3(),
|
|
3046
5473
|
zoom: 1
|
|
3047
5474
|
};
|
|
3048
5475
|
}
|
|
@@ -3106,13 +5533,13 @@ function normalizeI3SRequest(input) {
|
|
|
3106
5533
|
if (isTilesetRequest2(input)) {
|
|
3107
5534
|
return {
|
|
3108
5535
|
...input,
|
|
3109
|
-
basePath: input.basePath ||
|
|
5536
|
+
basePath: input.basePath || path_exports.dirname(input.url)
|
|
3110
5537
|
};
|
|
3111
5538
|
}
|
|
3112
5539
|
return {
|
|
3113
5540
|
url: input.url,
|
|
3114
5541
|
loader: input.loader,
|
|
3115
|
-
basePath: input.basePath ||
|
|
5542
|
+
basePath: input.basePath || path_exports.dirname(input.url),
|
|
3116
5543
|
resolver: input.resolver,
|
|
3117
5544
|
coreApi: input.coreApi
|
|
3118
5545
|
};
|
|
@@ -3294,8 +5721,7 @@ function normalizeTileError(error) {
|
|
|
3294
5721
|
}
|
|
3295
5722
|
|
|
3296
5723
|
// dist/tileset-2d/tileset-2d.js
|
|
3297
|
-
var
|
|
3298
|
-
var import_stats2 = require("@probe.gl/stats");
|
|
5724
|
+
var import_stats4 = require("@probe.gl/stats");
|
|
3299
5725
|
var STRATEGY_NEVER = "never";
|
|
3300
5726
|
var STRATEGY_REPLACE = "no-overlap";
|
|
3301
5727
|
var STRATEGY_DEFAULT = "best-available";
|
|
@@ -3320,7 +5746,7 @@ var DEFAULT_TILESET2D_PROPS = {
|
|
|
3320
5746
|
},
|
|
3321
5747
|
tileSource: null
|
|
3322
5748
|
};
|
|
3323
|
-
var Tileset2D = class {
|
|
5749
|
+
var Tileset2D = class _Tileset2D {
|
|
3324
5750
|
/** Live counters describing shared tileset state. */
|
|
3325
5751
|
stats;
|
|
3326
5752
|
/** Cached metadata returned by the backing TileSource, if any. */
|
|
@@ -3342,7 +5768,7 @@ var Tileset2D = class {
|
|
|
3342
5768
|
_lastTileContext = null;
|
|
3343
5769
|
/** Creates a tileset from either `getTileData` or a loaders.gl `TileSource`. */
|
|
3344
5770
|
constructor(opts) {
|
|
3345
|
-
this.stats = new
|
|
5771
|
+
this.stats = new import_stats4.Stats({
|
|
3346
5772
|
id: "Tileset2D",
|
|
3347
5773
|
stats: [
|
|
3348
5774
|
{ name: "Tiles In Cache" },
|
|
@@ -3360,7 +5786,7 @@ var Tileset2D = class {
|
|
|
3360
5786
|
getTileData: opts.getTileData || (() => null),
|
|
3361
5787
|
tileSource: opts.tileSource
|
|
3362
5788
|
};
|
|
3363
|
-
this._requestScheduler = new
|
|
5789
|
+
this._requestScheduler = new RequestScheduler({
|
|
3364
5790
|
throttleRequests: this.opts.maxRequests > 0 || this.opts.debounceTime > 0,
|
|
3365
5791
|
maxRequests: this.opts.maxRequests,
|
|
3366
5792
|
debounceTime: this.opts.debounceTime
|
|
@@ -3382,7 +5808,7 @@ var Tileset2D = class {
|
|
|
3382
5808
|
}
|
|
3383
5809
|
/** Convenience factory for wrapping a loaders.gl `TileSource`. */
|
|
3384
5810
|
static fromTileSource(tileSource, opts = {}) {
|
|
3385
|
-
return new
|
|
5811
|
+
return new _Tileset2D({ ...opts, tileSource });
|
|
3386
5812
|
}
|
|
3387
5813
|
/** All tiles currently present in the shared cache. */
|
|
3388
5814
|
get tiles() {
|
|
@@ -3453,7 +5879,7 @@ var Tileset2D = class {
|
|
|
3453
5879
|
const previousDebounceTime = this.opts.debounceTime;
|
|
3454
5880
|
this._applyResolvedOptions();
|
|
3455
5881
|
if (previousMaxRequests !== this.opts.maxRequests || previousDebounceTime !== this.opts.debounceTime) {
|
|
3456
|
-
this._requestScheduler = new
|
|
5882
|
+
this._requestScheduler = new RequestScheduler({
|
|
3457
5883
|
throttleRequests: this.opts.maxRequests > 0 || this.opts.debounceTime > 0,
|
|
3458
5884
|
maxRequests: this.opts.maxRequests,
|
|
3459
5885
|
debounceTime: this.opts.debounceTime
|
|
@@ -3823,7 +6249,7 @@ var DEFAULT_IMAGESET_PROPS = {
|
|
|
3823
6249
|
getImage: async () => null,
|
|
3824
6250
|
debounceTime: 0
|
|
3825
6251
|
};
|
|
3826
|
-
var ImageSet = class {
|
|
6252
|
+
var ImageSet = class _ImageSet {
|
|
3827
6253
|
/** Cached metadata returned by the backing source, if any. */
|
|
3828
6254
|
metadata = null;
|
|
3829
6255
|
_opts;
|
|
@@ -3844,7 +6270,7 @@ var ImageSet = class {
|
|
|
3844
6270
|
}
|
|
3845
6271
|
/** Convenience factory for wrapping a loaders.gl {@link ImageSource}. */
|
|
3846
6272
|
static fromImageSource(imageSource, opts = {}) {
|
|
3847
|
-
return new
|
|
6273
|
+
return new _ImageSet({ ...opts, imageSource });
|
|
3848
6274
|
}
|
|
3849
6275
|
/** Whether the manager has no requests in flight and a current image. */
|
|
3850
6276
|
get isLoaded() {
|
|
@@ -3956,7 +6382,7 @@ var ImageSet = class {
|
|
|
3956
6382
|
}
|
|
3957
6383
|
this._emitUpdate();
|
|
3958
6384
|
} catch (error) {
|
|
3959
|
-
if (
|
|
6385
|
+
if (isAbortError2(error)) {
|
|
3960
6386
|
return;
|
|
3961
6387
|
}
|
|
3962
6388
|
const normalizedError = normalizeImageSetError(error, "Image request failed");
|
|
@@ -4040,7 +6466,7 @@ var ImageSet = class {
|
|
|
4040
6466
|
this._abortController = null;
|
|
4041
6467
|
}
|
|
4042
6468
|
};
|
|
4043
|
-
function
|
|
6469
|
+
function isAbortError2(error) {
|
|
4044
6470
|
return error instanceof Error && error.name === "AbortError";
|
|
4045
6471
|
}
|
|
4046
6472
|
function normalizeImageSetError(error, message) {
|
|
@@ -4071,7 +6497,7 @@ var DEFAULT_RASTERSET_PROPS = {
|
|
|
4071
6497
|
debounceTime: 0,
|
|
4072
6498
|
shouldRefetch: () => true
|
|
4073
6499
|
};
|
|
4074
|
-
var RasterSet = class {
|
|
6500
|
+
var RasterSet = class _RasterSet {
|
|
4075
6501
|
/** Cached metadata returned by the backing source, if any. */
|
|
4076
6502
|
metadata = null;
|
|
4077
6503
|
_opts;
|
|
@@ -4092,7 +6518,7 @@ var RasterSet = class {
|
|
|
4092
6518
|
}
|
|
4093
6519
|
/** Convenience factory for wrapping a loaders.gl {@link RasterSource}. */
|
|
4094
6520
|
static fromRasterSource(rasterSource, opts = {}) {
|
|
4095
|
-
return new
|
|
6521
|
+
return new _RasterSet({ ...opts, rasterSource });
|
|
4096
6522
|
}
|
|
4097
6523
|
/** Whether the manager has no requests in flight and a current raster. */
|
|
4098
6524
|
get isLoaded() {
|
|
@@ -4215,7 +6641,7 @@ var RasterSet = class {
|
|
|
4215
6641
|
}
|
|
4216
6642
|
this._emitUpdate();
|
|
4217
6643
|
} catch (error) {
|
|
4218
|
-
if (
|
|
6644
|
+
if (isAbortError3(error)) {
|
|
4219
6645
|
return;
|
|
4220
6646
|
}
|
|
4221
6647
|
const normalizedError = normalizeRasterSetError(error, "Raster request failed");
|
|
@@ -4307,13 +6733,13 @@ function normalizeRasterSetError(error, message) {
|
|
|
4307
6733
|
}
|
|
4308
6734
|
return new Error(message);
|
|
4309
6735
|
}
|
|
4310
|
-
function
|
|
6736
|
+
function isAbortError3(error) {
|
|
4311
6737
|
return error instanceof Error && error.name === "AbortError";
|
|
4312
6738
|
}
|
|
4313
6739
|
|
|
4314
6740
|
// dist/point-cloud/point-cloud-tileset.js
|
|
4315
|
-
var
|
|
4316
|
-
var
|
|
6741
|
+
var import_core12 = require("@math.gl/core");
|
|
6742
|
+
var import_culling6 = require("@math.gl/culling");
|
|
4317
6743
|
|
|
4318
6744
|
// dist/point-cloud/point-cloud-tile.js
|
|
4319
6745
|
var PointCloudTile = class {
|
|
@@ -4381,7 +6807,7 @@ var PointCloudTile = class {
|
|
|
4381
6807
|
};
|
|
4382
6808
|
|
|
4383
6809
|
// dist/point-cloud/point-cloud-tileset.js
|
|
4384
|
-
var
|
|
6810
|
+
var DEFAULT_PROPS5 = {
|
|
4385
6811
|
debounceTime: 0,
|
|
4386
6812
|
minimumNodePixelSize: 150,
|
|
4387
6813
|
maximumScreenSpaceError: 1,
|
|
@@ -4411,7 +6837,7 @@ var PointCloudTileset = class {
|
|
|
4411
6837
|
tilesMap = /* @__PURE__ */ new Map();
|
|
4412
6838
|
constructor(dataSource, options = {}) {
|
|
4413
6839
|
this.dataSource = dataSource;
|
|
4414
|
-
this.options = { ...
|
|
6840
|
+
this.options = { ...DEFAULT_PROPS5, ...options };
|
|
4415
6841
|
this.tilesetInitializationPromise = this.initialize();
|
|
4416
6842
|
}
|
|
4417
6843
|
tilesetInitializationPromise;
|
|
@@ -4474,7 +6900,7 @@ var PointCloudTileset = class {
|
|
|
4474
6900
|
const viewState = await ((_b = (_a = this.dataSource).getViewState) == null ? void 0 : _b.call(_a));
|
|
4475
6901
|
this.boundingVolume = (viewState == null ? void 0 : viewState.boundingVolume) || this.root.boundingVolume;
|
|
4476
6902
|
const center = (viewState == null ? void 0 : viewState.cartographicCenter) || this.root.boundingVolume.center;
|
|
4477
|
-
this.cartographicCenter = new
|
|
6903
|
+
this.cartographicCenter = new import_core12.Vector3(center);
|
|
4478
6904
|
this.zoom = (viewState == null ? void 0 : viewState.zoom) || this.estimateZoom(this.boundingVolume);
|
|
4479
6905
|
}
|
|
4480
6906
|
/**
|
|
@@ -4486,9 +6912,9 @@ var PointCloudTileset = class {
|
|
|
4486
6912
|
this.lastUpdatedViewports = viewports;
|
|
4487
6913
|
}
|
|
4488
6914
|
if (!this.updatePromise) {
|
|
4489
|
-
this.updatePromise = new Promise((
|
|
6915
|
+
this.updatePromise = new Promise((resolve2, reject) => {
|
|
4490
6916
|
setTimeout(() => {
|
|
4491
|
-
this.doSelectTiles().then(() =>
|
|
6917
|
+
this.doSelectTiles().then(() => resolve2(this.frameNumberValue)).catch(reject).finally(() => {
|
|
4492
6918
|
this.updatePromise = null;
|
|
4493
6919
|
});
|
|
4494
6920
|
}, this.options.debounceTime);
|
|
@@ -4617,7 +7043,7 @@ var PointCloudTileset = class {
|
|
|
4617
7043
|
* Checks whether a common-space bounding sphere intersects a culling volume.
|
|
4618
7044
|
*/
|
|
4619
7045
|
isBoundingSphereInsideCullingVolume(boundingSphere, cullingVolume2) {
|
|
4620
|
-
return cullingVolume2.computeVisibilityWithPlaneMask(boundingSphere,
|
|
7046
|
+
return cullingVolume2.computeVisibilityWithPlaneMask(boundingSphere, import_culling6.CullingVolume.MASK_INDETERMINATE) !== import_culling6.CullingVolume.MASK_OUTSIDE;
|
|
4621
7047
|
}
|
|
4622
7048
|
/**
|
|
4623
7049
|
* Checks a projected screen-space footprint against the viewport rectangle.
|
|
@@ -4813,9 +7239,9 @@ var PointCloudTileset = class {
|
|
|
4813
7239
|
if (!plane) {
|
|
4814
7240
|
return null;
|
|
4815
7241
|
}
|
|
4816
|
-
planes.push(new
|
|
7242
|
+
planes.push(new import_culling6.Plane(plane.normal, -plane.distance));
|
|
4817
7243
|
}
|
|
4818
|
-
return new
|
|
7244
|
+
return new import_culling6.CullingVolume(planes);
|
|
4819
7245
|
}
|
|
4820
7246
|
/**
|
|
4821
7247
|
* Approximate a tile AABB by a common-space bounding sphere for frustum culling.
|
|
@@ -4852,7 +7278,7 @@ var PointCloudTileset = class {
|
|
|
4852
7278
|
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(minZ) || !Number.isFinite(maxX) || !Number.isFinite(maxY) || !Number.isFinite(maxZ)) {
|
|
4853
7279
|
return null;
|
|
4854
7280
|
}
|
|
4855
|
-
return new
|
|
7281
|
+
return new import_culling6.BoundingSphere().fromCornerPoints([minX, minY, minZ], [maxX, maxY, maxZ]);
|
|
4856
7282
|
}
|
|
4857
7283
|
};
|
|
4858
7284
|
//# sourceMappingURL=index.cjs.map
|