UNPKG

3d-tiles-renderer

Version:

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification

538 lines (537 loc) 20.5 kB
import { C as E, b as R, a as N, G as U, Q as S, z as M } from "./QuantizedMeshLoaderBase-DA143Nnz.js"; import { L as m, r as g, c as v } from "./LoaderBase-CfTLVHyZ.js"; function _(u) { return u.__implicitRoot.implicitTiling.subdivisionScheme === "OCTREE"; } function d(u) { return _(u) ? 8 : 4; } function y(u, e) { if (!e) return [0, 0, 0]; const i = 2 * e.__x + u.__subtreeIdx % 2, t = 2 * e.__y + Math.floor(u.__subtreeIdx / 2) % 2, r = _(u) ? 2 * e.__z + Math.floor(u.__subtreeIdx / 4) % 2 : 0; return [i, t, r]; } class p { constructor(e, i) { this.parent = e, this.children = [], this.__level = e.__level + 1, this.__implicitRoot = e.__implicitRoot, this.__subtreeIdx = i, [this.__x, this.__y, this.__z] = y(this, e); } static copy(e) { const i = {}; return i.children = [], i.__level = e.__level, i.__implicitRoot = e.__implicitRoot, i.__subtreeIdx = e.__subtreeIdx, [i.__x, i.__y, i.__z] = [e.__x, e.__y, e.__z], i.boundingVolume = e.boundingVolume, i.geometricError = e.geometricError, i; } } class A extends m { constructor(e) { super(), this.tile = e, this.rootTile = e.__implicitRoot, this.workingPath = null; } /** * A helper object for storing the two parts of the subtree binary * * @typedef {object} Subtree * @property {number} version * @property {JSON} subtreeJson * @property {ArrayBuffer} subtreeByte * @private */ /** * * @param buffer * @return {Subtree} */ parseBuffer(e) { const i = new DataView(e); let t = 0; const r = g(i); console.assert(r === "subt", 'SUBTREELoader: The magic bytes equal "subt".'), t += 4; const s = i.getUint32(t, !0); console.assert(s === 1, 'SUBTREELoader: The version listed in the header is "1".'), t += 4; const n = i.getUint32(t, !0); t += 8; const o = i.getUint32(t, !0); t += 8; const l = JSON.parse(v(new Uint8Array(e, t, n))); t += n; const a = e.slice(t, t + o); return { version: s, subtreeJson: l, subtreeByte: a }; } async parse(e) { const i = this.parseBuffer(e), t = i.subtreeJson; t.contentAvailabilityHeaders = [].concat(t.contentAvailability); const r = this.preprocessBuffers(t.buffers), s = this.preprocessBufferViews( t.bufferViews, r ); this.markActiveBufferViews(t, s); const n = await this.requestActiveBuffers( r, i.subtreeByte ), o = this.parseActiveBufferViews(s, n); this.parseAvailability(i, t, o), this.expandSubtree(this.tile, i); } /** * Determine which buffer views need to be loaded into memory. This includes: * * <ul> * <li>The tile availability bitstream (if a bitstream is defined)</li> * <li>The content availability bitstream(s) (if a bitstream is defined)</li> * <li>The child subtree availability bitstream (if a bitstream is defined)</li> * </ul> * * <p> * This function modifies the buffer view headers' isActive flags in place. * </p> * * @param {JSON} subtreeJson The JSON chunk from the subtree * @param {BufferViewHeader[]} bufferViewHeaders The preprocessed buffer view headers * @private */ markActiveBufferViews(e, i) { let t; const r = e.tileAvailability; isNaN(r.bitstream) ? isNaN(r.bufferView) || (t = i[r.bufferView]) : t = i[r.bitstream], t && (t.isActive = !0, t.bufferHeader.isActive = !0); const s = e.contentAvailabilityHeaders; for (let o = 0; o < s.length; o++) t = void 0, isNaN(s[o].bitstream) ? isNaN(s[o].bufferView) || (t = i[s[o].bufferView]) : t = i[s[o].bitstream], t && (t.isActive = !0, t.bufferHeader.isActive = !0); t = void 0; const n = e.childSubtreeAvailability; isNaN(n.bitstream) ? isNaN(n.bufferView) || (t = i[n.bufferView]) : t = i[n.bitstream], t && (t.isActive = !0, t.bufferHeader.isActive = !0); } /** * Go through the list of buffers and gather all the active ones into * a dictionary. * <p> * The results are put into a dictionary object. The keys are indices of * buffers, and the values are Uint8Arrays of the contents. Only buffers * marked with the isActive flag are fetched. * </p> * <p> * The internal buffer (the subtree's binary chunk) is also stored in this * dictionary if it is marked active. * </p> * @param {BufferHeader[]} bufferHeaders The preprocessed buffer headers * @param {ArrayBuffer} internalBuffer The binary chunk of the subtree file * @returns {object} buffersU8 A dictionary of buffer index to a Uint8Array of its contents. * @private */ async requestActiveBuffers(e, i) { const t = []; for (let n = 0; n < e.length; n++) { const o = e[n]; if (!o.isActive) t.push(Promise.resolve()); else if (o.isExternal) { const l = this.parseImplicitURIBuffer( this.tile, this.rootTile.implicitTiling.subtrees.uri, o.uri ), a = fetch(l, this.fetchOptions).then((c) => { if (!c.ok) throw new Error(`SUBTREELoader: Failed to load external buffer from ${o.uri} with error code ${c.status}.`); return c.arrayBuffer(); }).then((c) => new Uint8Array(c)); t.push(a); } else t.push(Promise.resolve(new Uint8Array(i))); } const r = await Promise.all(t), s = {}; for (let n = 0; n < r.length; n++) { const o = r[n]; o && (s[n] = o); } return s; } /** * Go through the list of buffer views, and if they are marked as active, * extract a subarray from one of the active buffers. * * @param {BufferViewHeader[]} bufferViewHeaders * @param {object} buffersU8 A dictionary of buffer index to a Uint8Array of its contents. * @returns {object} A dictionary of buffer view index to a Uint8Array of its contents. * @private */ parseActiveBufferViews(e, i) { const t = {}; for (let r = 0; r < e.length; r++) { const s = e[r]; if (!s.isActive) continue; const n = s.byteOffset, o = n + s.byteLength, l = i[s.buffer]; t[r] = l.slice(n, o); } return t; } /** * A buffer header is the JSON header from the subtree JSON chunk plus * a couple extra boolean flags for easy reference. * * Buffers are assumed inactive until explicitly marked active. This is used * to avoid fetching unneeded buffers. * * @typedef {object} BufferHeader * @property {boolean} isActive Whether this buffer is currently used. * @property {string} [uri] The URI of the buffer (external buffers only) * @property {number} byteLength The byte length of the buffer, including any padding contained within. * @private */ /** * Iterate over the list of buffers from the subtree JSON and add the isActive field for easier parsing later. * This modifies the objects in place. * @param {Object[]} [bufferHeaders=[]] The JSON from subtreeJson.buffers. * @returns {BufferHeader[]} The same array of headers with additional fields. * @private */ preprocessBuffers(e = []) { for (let i = 0; i < e.length; i++) { const t = e[i]; t.isActive = !1, t.isExternal = !!t.uri; } return e; } /** * A buffer view header is the JSON header from the subtree JSON chunk plus * the isActive flag and a reference to the header for the underlying buffer. * * @typedef {object} BufferViewHeader * @property {BufferHeader} bufferHeader A reference to the header for the underlying buffer * @property {boolean} isActive Whether this bufferView is currently used. * @property {number} buffer The index of the underlying buffer. * @property {number} byteOffset The start byte of the bufferView within the buffer. * @property {number} byteLength The length of the bufferView. No padding is included in this length. * @private */ /** * Iterate the list of buffer views from the subtree JSON and add the * isActive flag. Also save a reference to the bufferHeader. * * @param {Object[]} [bufferViewHeaders=[]] The JSON from subtree.bufferViews. * @param {BufferHeader[]} bufferHeaders The preprocessed buffer headers. * @returns {BufferViewHeader[]} The same array of bufferView headers with additional fields. * @private */ preprocessBufferViews(e = [], i) { for (let t = 0; t < e.length; t++) { const r = e[t]; r.bufferHeader = i[r.buffer], r.isActive = !1, r.isExternal = r.bufferHeader.isExternal; } return e; } /** * Parse the three availability bitstreams and store them in the subtree. * * @param {Subtree} subtree The subtree to modify. * @param {Object} subtreeJson The subtree JSON. * @param {Object} bufferViewsU8 A dictionary of buffer view index to a Uint8Array of its contents. * @private */ parseAvailability(e, i, t) { const r = d(this.rootTile), s = this.rootTile.implicitTiling.subtreeLevels, n = (Math.pow(r, s) - 1) / (r - 1), o = Math.pow(r, s); e._tileAvailability = this.parseAvailabilityBitstream( i.tileAvailability, t, n ), e._contentAvailabilityBitstreams = []; for (let l = 0; l < i.contentAvailabilityHeaders.length; l++) { const a = this.parseAvailabilityBitstream( i.contentAvailabilityHeaders[l], t, // content availability has the same length as tile availability. n ); e._contentAvailabilityBitstreams.push(a); } e._childSubtreeAvailability = this.parseAvailabilityBitstream( i.childSubtreeAvailability, t, o ); } /** * Given the JSON describing an availability bitstream, turn it into an * in-memory representation using an object. This handles bitstreams from a bufferView. * * @param {Object} availabilityJson A JSON object representing the availability. * @param {Object} bufferViewsU8 A dictionary of buffer view index to its Uint8Array contents. * @param {number} lengthBits The length of the availability bitstream in bits. * @returns {object} * @private */ parseAvailabilityBitstream(e, i, t) { if (!isNaN(e.constant)) return { constant: !!e.constant, lengthBits: t }; let r; return isNaN(e.bitstream) ? isNaN(e.bufferView) || (r = i[e.bufferView]) : r = i[e.bitstream], { bitstream: r, lengthBits: t }; } /** * Expand a single subtree tile. This transcodes the subtree into * a tree of {@link SubtreeTile}. The root of this tree is stored in * the placeholder tile's children array. This method also creates * tiles for the child subtrees to be lazily expanded as needed. * * @param {Object | SubtreeTile} subtreeRoot The first node of the subtree. * @param {Subtree} subtree The parsed subtree. * @private */ expandSubtree(e, i) { const t = p.copy(e); for (let n = 0; i && n < i._contentAvailabilityBitstreams.length; n++) if (i && this.getBit(i._contentAvailabilityBitstreams[n], 0)) { t.content = { uri: this.parseImplicitURI(e, this.rootTile.content.uri) }; break; } e.children.push(t); const r = this.transcodeSubtreeTiles( t, i ), s = this.listChildSubtrees(i, r); for (let n = 0; n < s.length; n++) { const o = s[n], l = o.tile, a = this.deriveChildTile( null, l, null, o.childMortonIndex ); a.content = { uri: this.parseImplicitURI(a, this.rootTile.implicitTiling.subtrees.uri) }, l.children.push(a); } } /** * Transcode the implicitly defined tiles within this subtree and generate * explicit {@link SubtreeTile} objects. This function only transcodes tiles, * child subtrees are handled separately. * * @param {Object | SubtreeTile} subtreeRoot The root of the current subtree. * @param {Subtree} subtree The subtree to get availability information. * @returns {Array} The bottom row of transcoded tiles. This is helpful for processing child subtrees. * @private */ transcodeSubtreeTiles(e, i) { let t = [e], r = []; for (let s = 1; s < this.rootTile.implicitTiling.subtreeLevels; s++) { const n = d(this.rootTile), o = (Math.pow(n, s) - 1) / (n - 1), l = n * t.length; for (let a = 0; a < l; a++) { const c = o + a, h = a >> Math.log2(n), f = t[h]; if (!this.getBit(i._tileAvailability, c)) { r.push(void 0); continue; } const b = this.deriveChildTile( i, f, c, a ); f.children.push(b), r.push(b); } t = r, r = []; } return t; } /** * Given a parent tile and information about which child to create, derive * the properties of the child tile implicitly. * <p> * This creates a real tile for rendering. * </p> * * @param {Subtree} subtree The subtree the child tile belongs to. * @param {Object | SubtreeTile} parentTile The parent of the new child tile. * @param {number} childBitIndex The index of the child tile within the tile's availability information. * @param {number} childMortonIndex The morton index of the child tile relative to its parent. * @returns {SubtreeTile} The new child tile. * @private */ deriveChildTile(e, i, t, r) { const s = new p(i, r); s.boundingVolume = this.getTileBoundingVolume(s), s.geometricError = this.getGeometricError(s); for (let n = 0; e && n < e._contentAvailabilityBitstreams.length; n++) if (e && this.getBit(e._contentAvailabilityBitstreams[n], t)) { s.content = { uri: this.parseImplicitURI(s, this.rootTile.content.uri) }; break; } return s; } /** * Get a bit from the bitstream as a Boolean. If the bitstream * is a constant, the constant value is returned instead. * * @param {ParsedBitstream} object * @param {number} index The integer index of the bit. * @returns {boolean} The value of the bit. * @private */ getBit(e, i) { if (i < 0 || i >= e.lengthBits) throw new Error("Bit index out of bounds."); if (e.constant !== void 0) return e.constant; const t = i >> 3, r = i % 8; return (new Uint8Array(e.bitstream)[t] >> r & 1) === 1; } /** * //TODO Adapt for Sphere * To maintain numerical stability during this subdivision process, * the actual bounding volumes should not be computed progressively by subdividing a non-root tile volume. * Instead, the exact bounding volumes are computed directly for a given level. * @param {Object | SubtreeTile} tile * @return {Object} object containing the bounding volume. */ getTileBoundingVolume(e) { const i = {}; if (this.rootTile.boundingVolume.region) { const t = [...this.rootTile.boundingVolume.region], r = t[0], s = t[2], n = t[1], o = t[3], l = (s - r) / Math.pow(2, e.__level), a = (o - n) / Math.pow(2, e.__level); t[0] = r + l * e.__x, t[2] = r + l * (e.__x + 1), t[1] = n + a * e.__y, t[3] = n + a * (e.__y + 1); for (let c = 0; c < 4; c++) { const h = t[c]; h < -Math.PI ? t[c] += 2 * Math.PI : h > Math.PI && (t[c] -= 2 * Math.PI); } if (_(e)) { const c = t[4], f = (t[5] - c) / Math.pow(2, e.__level); t[4] = c + f * e.__z, t[5] = c + f * (e.__z + 1); } i.region = t; } if (this.rootTile.boundingVolume.box) { const t = [...this.rootTile.boundingVolume.box], r = 2 ** e.__level - 1, s = Math.pow(2, -e.__level), n = _(e) ? 3 : 2; for (let o = 0; o < n; o++) { t[3 + o * 3 + 0] *= s, t[3 + o * 3 + 1] *= s, t[3 + o * 3 + 2] *= s; const l = t[3 + o * 3 + 0], a = t[3 + o * 3 + 1], c = t[3 + o * 3 + 2], h = o === 0 ? e.__x : o === 1 ? e.__y : e.__z; t[0] += 2 * l * (-0.5 * r + h), t[1] += 2 * a * (-0.5 * r + h), t[2] += 2 * c * (-0.5 * r + h); } i.box = t; } return i; } /** * Each child’s geometricError is half of its parent’s geometricError. * @param {Object | SubtreeTile} tile * @return {number} */ getGeometricError(e) { return this.rootTile.geometricError / Math.pow(2, e.__level); } /** * Determine what child subtrees exist and return a list of information. * * @param {Object} subtree The subtree for looking up availability. * @param {Array} bottomRow The bottom row of tiles in a transcoded subtree. * @returns {[]} A list of identifiers for the child subtrees. * @private */ listChildSubtrees(e, i) { const t = [], r = d(this.rootTile); for (let s = 0; s < i.length; s++) { const n = i[s]; if (n !== void 0) for (let o = 0; o < r; o++) { const l = s * r + o; this.getBit(e._childSubtreeAvailability, l) && t.push({ tile: n, childMortonIndex: l }); } } return t; } /** * Replaces placeholder tokens in a URI template with the corresponding tile properties. * * The URI template should contain the tokens: * - `{level}` for the tile's subdivision level. * - `{x}` for the tile's x-coordinate. * - `{y}` for the tile's y-coordinate. * - `{z}` for the tile's z-coordinate. * * @param {Object} tile - The tile object containing properties __level, __x, __y, and __z. * @param {string} uri - The URI template string with placeholders. * @returns {string} The URI with placeholders replaced by the tile's properties. */ parseImplicitURI(e, i) { return i = i.replace("{level}", e.__level), i = i.replace("{x}", e.__x), i = i.replace("{y}", e.__y), i = i.replace("{z}", e.__z), i; } /** * Generates the full external buffer URI for a tile by combining an implicit URI with a buffer URI. * * First, it parses the implicit URI using the tile properties and the provided template. Then, it creates a new URL * relative to the tile's base path, removes the last path segment, and appends the buffer URI. * * @param {Object} tile - The tile object that contains properties: * - __level: the subdivision level, * - __x, __y, __z: the tile coordinates, * @param {string} uri - The URI template string with placeholders for the tile (e.g., `{level}`, `{x}`, `{y}`, `{z}`). * @param {string} bufUri - The buffer file name to append (e.g., "0_1.bin"). * @returns {string} The full external buffer URI. */ parseImplicitURIBuffer(e, i, t) { const r = this.parseImplicitURI(e, i), s = new URL(r, this.workingPath + "/"); return s.pathname = s.pathname.substring(0, s.pathname.lastIndexOf("/")), new URL(s.pathname + "/" + t, this.workingPath + "/").toString(); } } class x { constructor() { this.name = "IMPLICIT_TILING_PLUGIN"; } init(e) { this.tiles = e; } preprocessNode(e, i, t) { var r; e.implicitTiling ? (e.__hasUnrenderableContent = !0, e.__hasRenderableContent = !1, e.__subtreeIdx = 0, e.__implicitRoot = e, e.__x = 0, e.__y = 0, e.__z = 0, e.__level = 0) : /.subtree$/i.test((r = e.content) == null ? void 0 : r.uri) && (e.__hasUnrenderableContent = !0, e.__hasRenderableContent = !1); } parseTile(e, i, t) { if (/^subtree$/i.test(t)) { const r = new A(i); return r.workingPath = i.__basePath, r.fetchOptions = this.tiles.fetchOptions, r.parse(e); } } preprocessURL(e, i) { if (i && i.implicitTiling) { const t = i.implicitTiling.subtrees.uri.replace("{level}", i.__level).replace("{x}", i.__x).replace("{y}", i.__y).replace("{z}", i.__z); return new URL(t, i.__basePath + "/").toString(); } return e; } disposeTile(e) { var i; /.subtree$/i.test((i = e.content) == null ? void 0 : i.uri) && (e.children.forEach((t) => { this.tiles.processNodeQueue.remove(t); }), e.children.length = 0, e.__childrenProcessed = 0); } } class T { constructor() { this.name = "ENFORCE_NONZERO_ERROR", this.priority = -1 / 0, this.originalError = /* @__PURE__ */ new Map(); } preprocessNode(e) { if (e.geometricError === 0) { let i = e.parent, t = 1; for (; i !== null; ) { if (i.geometricError !== 0) { e.geometricError = i.geometricError * 2 ** -t; break; } i = i.parent, t++; } } } } export { E as CesiumIonAuth, R as CesiumIonAuthPlugin, T as EnforceNonZeroErrorPlugin, N as GoogleCloudAuth, U as GoogleCloudAuthPlugin, x as ImplicitTilingPlugin, S as QuantizedMeshLoaderBase, M as zigZagDecode }; //# sourceMappingURL=index.core-plugins.js.map