智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  1. import Check from "../Core/Check.js";
  2. import DeveloperError from "../Core/DeveloperError.js";
  3. import defined from "../Core/defined.js";
  4. import destroyObject from "../Core/destroyObject.js";
  5. import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";
  6. import RuntimeError from "../Core/RuntimeError.js";
  7. import hasExtension from "./hasExtension.js";
  8. import ImplicitAvailabilityBitstream from "./ImplicitAvailabilityBitstream.js";
  9. import ImplicitMetadataView from "./ImplicitMetadataView.js";
  10. import ImplicitSubdivisionScheme from "./ImplicitSubdivisionScheme.js";
  11. import ImplicitSubtreeMetadata from "./ImplicitSubtreeMetadata.js";
  12. import MetadataTable from "./MetadataTable.js";
  13. import ResourceCache from "./ResourceCache.js";
  14. /**
  15. * An object representing a single subtree in an implicit tileset
  16. * including availability.
  17. * <p>
  18. * Subtrees handle tile metadata, defined in the subtree JSON in either
  19. * tileMetadata (3D Tiles 1.1) or the <code>3DTILES_metadata</code> extension.
  20. * Subtrees also handle content metadata and metadata about the subtree itself.
  21. * </p>
  22. *
  23. * This object is normally not instantiated directly, use {@link ImplicitSubtree.fromSubtreeJson}.
  24. *
  25. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_metadata#implicit-tile-properties|Implicit Tile Properties in the 3DTILES_metadata specification}
  26. * @see ImplicitSubtree.fromSubtreeJson
  27. *
  28. * @alias ImplicitSubtree
  29. * @constructor
  30. *
  31. * @param {Resource} resource The resource for this subtree. This is used for fetching external buffers as needed.
  32. * @param {ImplicitTileset} implicitTileset The implicit tileset. This includes information about the size of subtrees
  33. * @param {ImplicitTileCoordinates} implicitCoordinates The coordinates of the subtree's root tile.
  34. *
  35. * @private
  36. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  37. */
  38. function ImplicitSubtree(resource, implicitTileset, implicitCoordinates) {
  39. //>>includeStart('debug', pragmas.debug);
  40. Check.typeOf.object("resource", resource);
  41. Check.typeOf.object("implicitTileset", implicitTileset);
  42. Check.typeOf.object("implicitCoordinates", implicitCoordinates);
  43. //>>includeEnd('debug');
  44. this._resource = resource;
  45. this._subtreeJson = undefined;
  46. this._bufferLoader = undefined;
  47. this._tileAvailability = undefined;
  48. this._contentAvailabilityBitstreams = [];
  49. this._childSubtreeAvailability = undefined;
  50. this._implicitCoordinates = implicitCoordinates;
  51. this._subtreeLevels = implicitTileset.subtreeLevels;
  52. this._subdivisionScheme = implicitTileset.subdivisionScheme;
  53. this._branchingFactor = implicitTileset.branchingFactor;
  54. // properties for metadata
  55. this._metadata = undefined;
  56. this._tileMetadataTable = undefined;
  57. this._tilePropertyTableJson = undefined;
  58. this._contentMetadataTables = [];
  59. this._contentPropertyTableJsons = [];
  60. // Jump buffers are maps of availability bit index to entity ID
  61. this._tileJumpBuffer = undefined;
  62. this._contentJumpBuffers = [];
  63. this._ready = false;
  64. }
  65. Object.defineProperties(ImplicitSubtree.prototype, {
  66. /**
  67. * Returns true once all necessary availability buffers
  68. * are loaded.
  69. *
  70. * @type {boolean}
  71. * @readonly
  72. * @private
  73. */
  74. ready: {
  75. get: function () {
  76. return this._ready;
  77. },
  78. },
  79. /**
  80. * When subtree metadata is present (3D Tiles 1.1), this property stores an {@link ImplicitSubtreeMetadata} instance
  81. *
  82. * @type {ImplicitSubtreeMetadata}
  83. * @readonly
  84. * @private
  85. */
  86. metadata: {
  87. get: function () {
  88. return this._metadata;
  89. },
  90. },
  91. /**
  92. * When tile metadata is present (3D Tiles 1.1) or the <code>3DTILES_metadata</code> extension is used,
  93. * this property stores a {@link MetadataTable} instance for the tiles in the subtree.
  94. *
  95. * @type {MetadataTable}
  96. * @readonly
  97. * @private
  98. */
  99. tileMetadataTable: {
  100. get: function () {
  101. return this._tileMetadataTable;
  102. },
  103. },
  104. /**
  105. * When tile metadata is present (3D Tiles 1.1) or the <code>3DTILES_metadata</code> extension is used,
  106. * this property stores the JSON from the extension. This is used by {@link TileMetadata}
  107. * to get the extras and extensions for the tiles in the subtree.
  108. *
  109. * @type {object}
  110. * @readonly
  111. * @private
  112. */
  113. tilePropertyTableJson: {
  114. get: function () {
  115. return this._tilePropertyTableJson;
  116. },
  117. },
  118. /**
  119. * When content metadata is present (3D Tiles 1.1), this property stores
  120. * an array of {@link MetadataTable} instances for the contents in the subtree.
  121. *
  122. * @type {Array}
  123. * @readonly
  124. * @private
  125. */
  126. contentMetadataTables: {
  127. get: function () {
  128. return this._contentMetadataTables;
  129. },
  130. },
  131. /**
  132. * When content metadata is present (3D Tiles 1.1), this property
  133. * an array of the JSONs from the extension. This is used to get the extras
  134. * and extensions for the contents in the subtree.
  135. *
  136. * @type {Array}
  137. * @readonly
  138. * @private
  139. */
  140. contentPropertyTableJsons: {
  141. get: function () {
  142. return this._contentPropertyTableJsons;
  143. },
  144. },
  145. /**
  146. * Gets the implicit tile coordinates for the root of the subtree.
  147. *
  148. * @type {ImplicitTileCoordinates}
  149. * @readonly
  150. * @private
  151. */
  152. implicitCoordinates: {
  153. get: function () {
  154. return this._implicitCoordinates;
  155. },
  156. },
  157. });
  158. /**
  159. * Check if a specific tile is available at an index of the tile availability bitstream
  160. *
  161. * @param {number} index The index of the desired tile
  162. * @returns {boolean} The value of the i-th bit
  163. * @private
  164. */
  165. ImplicitSubtree.prototype.tileIsAvailableAtIndex = function (index) {
  166. return this._tileAvailability.getBit(index);
  167. };
  168. /**
  169. * Check if a specific tile is available at an implicit tile coordinate
  170. * NOTE: only used for voxels.
  171. *
  172. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a tile
  173. * @returns {boolean} The value of the i-th bit
  174. * @private
  175. */
  176. ImplicitSubtree.prototype.tileIsAvailableAtCoordinates = function (
  177. implicitCoordinates,
  178. ) {
  179. const index = this.getTileIndex(implicitCoordinates);
  180. return this.tileIsAvailableAtIndex(index);
  181. };
  182. /**
  183. * Check if a specific tile's content is available at an index of the content availability bitstream
  184. *
  185. * @param {number} index The index of the desired tile
  186. * @param {number} [contentIndex=0] The index of the desired content when multiple contents are used.
  187. * @returns {boolean} The value of the i-th bit
  188. * @private
  189. */
  190. ImplicitSubtree.prototype.contentIsAvailableAtIndex = function (
  191. index,
  192. contentIndex,
  193. ) {
  194. contentIndex = contentIndex ?? 0;
  195. //>>includeStart('debug', pragmas.debug);
  196. if (
  197. contentIndex < 0 ||
  198. contentIndex >= this._contentAvailabilityBitstreams.length
  199. ) {
  200. throw new DeveloperError("contentIndex out of bounds.");
  201. }
  202. //>>includeEnd('debug');
  203. return this._contentAvailabilityBitstreams[contentIndex].getBit(index);
  204. };
  205. /**
  206. * Check if a specific tile's content is available at an implicit tile coordinate
  207. *
  208. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a tile
  209. * @param {number} [contentIndex=0] The index of the desired content when the <code>3DTILES_multiple_contents</code> extension is used.
  210. * @returns {boolean} The value of the i-th bit
  211. * @private
  212. */
  213. ImplicitSubtree.prototype.contentIsAvailableAtCoordinates = function (
  214. implicitCoordinates,
  215. contentIndex,
  216. ) {
  217. const index = this.getTileIndex(implicitCoordinates);
  218. return this.contentIsAvailableAtIndex(index, contentIndex);
  219. };
  220. /**
  221. * Check if a child subtree is available at an index of the child subtree availability bitstream
  222. *
  223. * @param {number} index The index of the desired child subtree
  224. * @returns {boolean} The value of the i-th bit
  225. * @private
  226. */
  227. ImplicitSubtree.prototype.childSubtreeIsAvailableAtIndex = function (index) {
  228. return this._childSubtreeAvailability.getBit(index);
  229. };
  230. /**
  231. * Check if a specific child subtree is available at an implicit tile coordinate
  232. * NOTE: only used for voxels.
  233. *
  234. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a child subtree
  235. * @returns {boolean} The value of the i-th bit
  236. * @private
  237. */
  238. ImplicitSubtree.prototype.childSubtreeIsAvailableAtCoordinates = function (
  239. implicitCoordinates,
  240. ) {
  241. const index = this.getChildSubtreeIndex(implicitCoordinates);
  242. return this.childSubtreeIsAvailableAtIndex(index);
  243. };
  244. /**
  245. * Get the index of the first node at the given level within this subtree.
  246. * e.g. for a quadtree:
  247. * <ul>
  248. * <li>Level 0 starts at index 0</li>
  249. * <li>Level 1 starts at index 1</li>
  250. * <li>Level 2 starts at index 5</li>
  251. * </ul>
  252. *
  253. * @param {number} level The 0-indexed level number relative to the root of the subtree
  254. * @returns {number} The first index at the desired level
  255. * @private
  256. */
  257. ImplicitSubtree.prototype.getLevelOffset = function (level) {
  258. const branchingFactor = this._branchingFactor;
  259. return (Math.pow(branchingFactor, level) - 1) / (branchingFactor - 1);
  260. };
  261. /**
  262. * Get the morton index of a tile's parent. This is equivalent to
  263. * chopping off the last 2 (quadtree) or 3 (octree) bits of the morton
  264. * index.
  265. *
  266. * @param {number} childIndex The morton index of the child tile relative to its parent
  267. * @returns {number} The index of the child's parent node
  268. * @private
  269. */
  270. ImplicitSubtree.prototype.getParentMortonIndex = function (mortonIndex) {
  271. let bitsPerLevel = 2;
  272. if (this._subdivisionScheme === ImplicitSubdivisionScheme.OCTREE) {
  273. bitsPerLevel = 3;
  274. }
  275. return mortonIndex >> bitsPerLevel;
  276. };
  277. /**
  278. * Parse all relevant information out of the subtree. This fetches any
  279. * external buffers that are used by the implicit tileset.
  280. *
  281. * @param {Resource} resource The resource for this subtree. This is used for fetching external buffers as needed.
  282. * @param {object} [json] The JSON object for this subtree. If parsing from a binary subtree file, this will be undefined.
  283. * @param {Uint8Array} [subtreeView] The contents of the subtree binary
  284. * @param {ImplicitTileset} implicitTileset The implicit tileset this subtree belongs to.
  285. * @param {ImplicitTileCoordinates} implicitCoordinates The coordinates of the subtree's root tile.
  286. * @return {Promise<ImplicitSubtree>} The created subtree
  287. * @private
  288. *
  289. * @exception {DeveloperError} One of json and subtreeView must be defined.
  290. */
  291. ImplicitSubtree.fromSubtreeJson = async function (
  292. resource,
  293. json,
  294. subtreeView,
  295. implicitTileset,
  296. implicitCoordinates,
  297. ) {
  298. //>>includeStart('debug', pragmas.debug);
  299. Check.typeOf.object("resource", resource);
  300. if (defined(json) === defined(subtreeView)) {
  301. throw new DeveloperError("One of json and subtreeView must be defined.");
  302. }
  303. Check.typeOf.object("implicitTileset", implicitTileset);
  304. Check.typeOf.object("implicitCoordinates", implicitCoordinates);
  305. //>>includeEnd('debug');
  306. const subtree = new ImplicitSubtree(
  307. resource,
  308. implicitTileset,
  309. implicitCoordinates,
  310. );
  311. let chunks;
  312. if (defined(json)) {
  313. chunks = {
  314. json: json,
  315. binary: undefined,
  316. };
  317. } else {
  318. chunks = parseSubtreeChunks(subtreeView);
  319. }
  320. const subtreeJson = chunks.json;
  321. subtree._subtreeJson = subtreeJson;
  322. let tilePropertyTableJson;
  323. if (hasExtension(subtreeJson, "3DTILES_metadata")) {
  324. tilePropertyTableJson = subtreeJson.extensions["3DTILES_metadata"];
  325. } else if (defined(subtreeJson.tileMetadata)) {
  326. const propertyTableIndex = subtreeJson.tileMetadata;
  327. tilePropertyTableJson = subtreeJson.propertyTables[propertyTableIndex];
  328. }
  329. const contentPropertyTableJsons = [];
  330. if (defined(subtreeJson.contentMetadata)) {
  331. const length = subtreeJson.contentMetadata.length;
  332. for (let i = 0; i < length; i++) {
  333. const propertyTableIndex = subtreeJson.contentMetadata[i];
  334. contentPropertyTableJsons.push(
  335. subtreeJson.propertyTables[propertyTableIndex],
  336. );
  337. }
  338. }
  339. let metadata;
  340. const schema = implicitTileset.metadataSchema;
  341. const subtreeMetadata = subtreeJson.subtreeMetadata;
  342. if (defined(subtreeMetadata)) {
  343. const metadataClass = subtreeMetadata.class;
  344. const subtreeMetadataClass = schema.classes[metadataClass];
  345. metadata = new ImplicitSubtreeMetadata({
  346. subtreeMetadata: subtreeMetadata,
  347. class: subtreeMetadataClass,
  348. });
  349. }
  350. subtree._metadata = metadata;
  351. subtree._tilePropertyTableJson = tilePropertyTableJson;
  352. subtree._contentPropertyTableJsons = contentPropertyTableJsons;
  353. // if no contentAvailability is specified, no tile in the subtree has
  354. // content
  355. const defaultContentAvailability = {
  356. constant: 0,
  357. };
  358. // In 3D Tiles 1.1, content availability is provided in an array in the subtree JSON
  359. // regardless of whether or not it contains multiple contents. This differs from previous
  360. // schemas, where content availability is either a single object in the subtree JSON or
  361. // as an array in the 3DTILES_multiple_contents extension.
  362. //
  363. // After identifying how availability is stored, put the results in this new array for consistent processing later
  364. subtreeJson.contentAvailabilityHeaders = [];
  365. if (hasExtension(subtreeJson, "3DTILES_multiple_contents")) {
  366. subtreeJson.contentAvailabilityHeaders =
  367. subtreeJson.extensions["3DTILES_multiple_contents"].contentAvailability;
  368. } else if (Array.isArray(subtreeJson.contentAvailability)) {
  369. subtreeJson.contentAvailabilityHeaders = subtreeJson.contentAvailability;
  370. } else {
  371. subtreeJson.contentAvailabilityHeaders.push(
  372. subtreeJson.contentAvailability ?? defaultContentAvailability,
  373. );
  374. }
  375. const bufferHeaders = preprocessBuffers(subtreeJson.buffers);
  376. const bufferViewHeaders = preprocessBufferViews(
  377. subtreeJson.bufferViews,
  378. bufferHeaders,
  379. );
  380. // Buffers and buffer views are inactive until explicitly marked active.
  381. // This way we can avoid fetching buffers that will not be used.
  382. markActiveBufferViews(subtreeJson, bufferViewHeaders);
  383. if (defined(tilePropertyTableJson)) {
  384. markActiveMetadataBufferViews(tilePropertyTableJson, bufferViewHeaders);
  385. }
  386. for (let i = 0; i < contentPropertyTableJsons.length; i++) {
  387. const contentPropertyTableJson = contentPropertyTableJsons[i];
  388. markActiveMetadataBufferViews(contentPropertyTableJson, bufferViewHeaders);
  389. }
  390. const buffersU8 = await requestActiveBuffers(
  391. subtree,
  392. bufferHeaders,
  393. chunks.binary,
  394. );
  395. const bufferViewsU8 = parseActiveBufferViews(bufferViewHeaders, buffersU8);
  396. parseAvailability(subtree, subtreeJson, implicitTileset, bufferViewsU8);
  397. if (defined(tilePropertyTableJson)) {
  398. parseTileMetadataTable(subtree, implicitTileset, bufferViewsU8);
  399. makeTileJumpBuffer(subtree);
  400. }
  401. parseContentMetadataTables(subtree, implicitTileset, bufferViewsU8);
  402. makeContentJumpBuffers(subtree);
  403. subtree._ready = true;
  404. return subtree;
  405. };
  406. /**
  407. * A helper object for storing the two parts of the subtree binary
  408. *
  409. * @typedef {object} SubtreeChunks
  410. * @property {object} json The json chunk of the subtree
  411. * @property {Uint8Array} binary The binary chunk of the subtree. This represents the internal buffer.
  412. * @private
  413. */
  414. /**
  415. * Given the binary contents of a subtree, split into JSON and binary chunks
  416. *
  417. * @param {Uint8Array} subtreeView The subtree binary
  418. * @returns {SubtreeChunks} An object containing the JSON and binary chunks.
  419. * @private
  420. */
  421. function parseSubtreeChunks(subtreeView) {
  422. // Parse the header
  423. const littleEndian = true;
  424. const subtreeReader = new DataView(
  425. subtreeView.buffer,
  426. subtreeView.byteOffset,
  427. );
  428. // Skip to the chunk lengths
  429. let byteOffset = 8;
  430. // Read the bottom 32 bits of the 64-bit byte length. This is ok for now because:
  431. // 1) not all browsers have native 64-bit operations
  432. // 2) the data is well under 4GB
  433. const jsonByteLength = subtreeReader.getUint32(byteOffset, littleEndian);
  434. byteOffset += 8;
  435. const binaryByteLength = subtreeReader.getUint32(byteOffset, littleEndian);
  436. byteOffset += 8;
  437. const subtreeJson = getJsonFromTypedArray(
  438. subtreeView,
  439. byteOffset,
  440. jsonByteLength,
  441. );
  442. byteOffset += jsonByteLength;
  443. const subtreeBinary = subtreeView.subarray(
  444. byteOffset,
  445. byteOffset + binaryByteLength,
  446. );
  447. return {
  448. json: subtreeJson,
  449. binary: subtreeBinary,
  450. };
  451. }
  452. /**
  453. * A buffer header is the JSON header from the subtree JSON chunk plus
  454. * a couple extra boolean flags for easy reference.
  455. *
  456. * Buffers are assumed inactive until explicitly marked active. This is used
  457. * to avoid fetching unneeded buffers.
  458. *
  459. * @typedef {object} BufferHeader
  460. * @property {boolean} isExternal True if this is an external buffer
  461. * @property {boolean} isActive Whether this buffer is currently used.
  462. * @property {string} [uri] The URI of the buffer (external buffers only)
  463. * @property {number} byteLength The byte length of the buffer, including any padding contained within.
  464. * @private
  465. */
  466. /**
  467. * Iterate over the list of buffers from the subtree JSON and add the
  468. * isExternal and isActive fields for easier parsing later. This modifies
  469. * the objects in place.
  470. *
  471. * @param {object[]} [bufferHeaders=[]] The JSON from subtreeJson.buffers.
  472. * @returns {BufferHeader[]} The same array of headers with additional fields.
  473. * @private
  474. */
  475. function preprocessBuffers(bufferHeaders) {
  476. bufferHeaders = defined(bufferHeaders) ? bufferHeaders : [];
  477. for (let i = 0; i < bufferHeaders.length; i++) {
  478. const bufferHeader = bufferHeaders[i];
  479. bufferHeader.isExternal = defined(bufferHeader.uri);
  480. bufferHeader.isActive = false;
  481. }
  482. return bufferHeaders;
  483. }
  484. /**
  485. * A buffer header is the JSON header from the subtree JSON chunk plus
  486. * the isActive flag and a reference to the header for the underlying buffer
  487. *
  488. * @typedef {object} BufferViewHeader
  489. * @property {BufferHeader} bufferHeader A reference to the header for the underlying buffer
  490. * @property {boolean} isActive Whether this bufferView is currently used.
  491. * @property {number} buffer The index of the underlying buffer.
  492. * @property {number} byteOffset The start byte of the bufferView within the buffer.
  493. * @property {number} byteLength The length of the bufferView. No padding is included in this length.
  494. * @private
  495. */
  496. /**
  497. * Iterate the list of buffer views from the subtree JSON and add the
  498. * isActive flag. Also save a reference to the bufferHeader
  499. *
  500. * @param {object[]} [bufferViewHeaders=[]] The JSON from subtree.bufferViews
  501. * @param {BufferHeader[]} bufferHeaders The preprocessed buffer headers
  502. * @returns {BufferViewHeader[]} The same array of bufferView headers with additional fields
  503. * @private
  504. */
  505. function preprocessBufferViews(bufferViewHeaders, bufferHeaders) {
  506. bufferViewHeaders = defined(bufferViewHeaders) ? bufferViewHeaders : [];
  507. for (let i = 0; i < bufferViewHeaders.length; i++) {
  508. const bufferViewHeader = bufferViewHeaders[i];
  509. const bufferHeader = bufferHeaders[bufferViewHeader.buffer];
  510. bufferViewHeader.bufferHeader = bufferHeader;
  511. bufferViewHeader.isActive = false;
  512. }
  513. return bufferViewHeaders;
  514. }
  515. /**
  516. * Determine which buffer views need to be loaded into memory. This includes:
  517. *
  518. * <ul>
  519. * <li>The tile availability bitstream (if a bitstream is defined)</li>
  520. * <li>The content availability bitstream(s) (if a bitstream is defined)</li>
  521. * <li>The child subtree availability bitstream (if a bitstream is defined)</li>
  522. * </ul>
  523. *
  524. * <p>
  525. * This function modifies the buffer view headers' isActive flags in place.
  526. * </p>
  527. *
  528. * @param {object[]} subtreeJson The JSON chunk from the subtree
  529. * @param {BufferViewHeader[]} bufferViewHeaders The preprocessed buffer view headers
  530. * @private
  531. */
  532. function markActiveBufferViews(subtreeJson, bufferViewHeaders) {
  533. let header;
  534. const tileAvailabilityHeader = subtreeJson.tileAvailability;
  535. // Check for bitstream first, which is part of the current schema.
  536. // bufferView is the name of the bitstream from an older schema.
  537. if (defined(tileAvailabilityHeader.bitstream)) {
  538. header = bufferViewHeaders[tileAvailabilityHeader.bitstream];
  539. } else if (defined(tileAvailabilityHeader.bufferView)) {
  540. header = bufferViewHeaders[tileAvailabilityHeader.bufferView];
  541. }
  542. if (defined(header)) {
  543. header.isActive = true;
  544. header.bufferHeader.isActive = true;
  545. }
  546. const contentAvailabilityHeaders = subtreeJson.contentAvailabilityHeaders;
  547. for (let i = 0; i < contentAvailabilityHeaders.length; i++) {
  548. header = undefined;
  549. if (defined(contentAvailabilityHeaders[i].bitstream)) {
  550. header = bufferViewHeaders[contentAvailabilityHeaders[i].bitstream];
  551. } else if (defined(contentAvailabilityHeaders[i].bufferView)) {
  552. header = bufferViewHeaders[contentAvailabilityHeaders[i].bufferView];
  553. }
  554. if (defined(header)) {
  555. header.isActive = true;
  556. header.bufferHeader.isActive = true;
  557. }
  558. }
  559. header = undefined;
  560. const childSubtreeAvailabilityHeader = subtreeJson.childSubtreeAvailability;
  561. if (defined(childSubtreeAvailabilityHeader.bitstream)) {
  562. header = bufferViewHeaders[childSubtreeAvailabilityHeader.bitstream];
  563. } else if (defined(childSubtreeAvailabilityHeader.bufferView)) {
  564. header = bufferViewHeaders[childSubtreeAvailabilityHeader.bufferView];
  565. }
  566. if (defined(header)) {
  567. header.isActive = true;
  568. header.bufferHeader.isActive = true;
  569. }
  570. }
  571. /**
  572. * For handling metadata, look over the tile and content metadata buffers
  573. * <p>
  574. * This always loads all of the metadata immediately. Future iterations may
  575. * allow filtering this to avoid downloading unneeded buffers.
  576. * </p>
  577. *
  578. * @param {object} propertyTableJson The property table JSON for either a tile or some content
  579. * @param {BufferViewHeader[]} bufferViewHeaders The preprocessed buffer view headers
  580. * @private
  581. */
  582. function markActiveMetadataBufferViews(propertyTableJson, bufferViewHeaders) {
  583. const properties = propertyTableJson.properties;
  584. let header;
  585. for (const key in properties) {
  586. if (properties.hasOwnProperty(key)) {
  587. const metadataHeader = properties[key];
  588. // An older spec used bufferView
  589. const valuesBufferView =
  590. metadataHeader.values ?? metadataHeader.bufferView;
  591. header = bufferViewHeaders[valuesBufferView];
  592. header.isActive = true;
  593. header.bufferHeader.isActive = true;
  594. // An older spec used stringOffsetBufferView
  595. const stringOffsetBufferView =
  596. metadataHeader.stringOffsets ?? metadataHeader.stringOffsetBufferView;
  597. if (defined(stringOffsetBufferView)) {
  598. header = bufferViewHeaders[stringOffsetBufferView];
  599. header.isActive = true;
  600. header.bufferHeader.isActive = true;
  601. }
  602. // an older spec used arrayOffsetBufferView
  603. const arrayOffsetBufferView =
  604. metadataHeader.arrayOffsets ?? metadataHeader.arrayOffsetBufferView;
  605. if (defined(arrayOffsetBufferView)) {
  606. header = bufferViewHeaders[arrayOffsetBufferView];
  607. header.isActive = true;
  608. header.bufferHeader.isActive = true;
  609. }
  610. }
  611. }
  612. }
  613. /**
  614. * Go through the list of buffers and gather all the active ones into a
  615. * a dictionary. Since external buffers are allowed, this sometimes involves
  616. * fetching separate binary files. Consequently, this method returns a promise.
  617. * <p>
  618. * The results are put into a dictionary object. The keys are indices of
  619. * buffers, and the values are Uint8Arrays of the contents. Only buffers
  620. * marked with the isActive flag are fetched.
  621. * </p>
  622. * <p>
  623. * The internal buffer (the subtree's binary chunk) is also stored in this
  624. * dictionary if it is marked active.
  625. * </p>
  626. * @param {ImplicitSubtree} subtree The subtree
  627. * @param {BufferHeader[]} bufferHeaders The preprocessed buffer headers
  628. * @param {Uint8Array} internalBuffer The binary chunk of the subtree file
  629. * @returns {Promise<object>} A promise resolving to the dictionary of active buffers
  630. * @private
  631. */
  632. function requestActiveBuffers(subtree, bufferHeaders, internalBuffer) {
  633. const promises = [];
  634. for (let i = 0; i < bufferHeaders.length; i++) {
  635. const bufferHeader = bufferHeaders[i];
  636. if (!bufferHeader.isActive) {
  637. promises.push(Promise.resolve(undefined));
  638. } else if (bufferHeader.isExternal) {
  639. const promise = requestExternalBuffer(subtree, bufferHeader);
  640. promises.push(promise);
  641. } else {
  642. promises.push(Promise.resolve(internalBuffer));
  643. }
  644. }
  645. return Promise.all(promises).then(function (bufferResults) {
  646. const buffersU8 = {};
  647. for (let i = 0; i < bufferResults.length; i++) {
  648. const result = bufferResults[i];
  649. if (defined(result)) {
  650. buffersU8[i] = result;
  651. }
  652. }
  653. return buffersU8;
  654. });
  655. }
  656. async function requestExternalBuffer(subtree, bufferHeader) {
  657. const baseResource = subtree._resource;
  658. const bufferResource = baseResource.getDerivedResource({
  659. url: bufferHeader.uri,
  660. });
  661. const bufferLoader = ResourceCache.getExternalBufferLoader({
  662. resource: bufferResource,
  663. });
  664. subtree._bufferLoader = bufferLoader;
  665. try {
  666. await bufferLoader.load();
  667. } catch (error) {
  668. if (bufferLoader.isDestroyed()) {
  669. return;
  670. }
  671. throw error;
  672. }
  673. return bufferLoader.typedArray;
  674. }
  675. /**
  676. * Go through the list of buffer views, and if they are marked as active,
  677. * extract a subarray from one of the active buffers.
  678. *
  679. * @param {BufferViewHeader[]} bufferViewHeaders
  680. * @param {object} buffersU8 A dictionary of buffer index to a Uint8Array of its contents.
  681. * @returns {object} A dictionary of buffer view index to a Uint8Array of its contents.
  682. * @private
  683. */
  684. function parseActiveBufferViews(bufferViewHeaders, buffersU8) {
  685. const bufferViewsU8 = {};
  686. for (let i = 0; i < bufferViewHeaders.length; i++) {
  687. const bufferViewHeader = bufferViewHeaders[i];
  688. if (!bufferViewHeader.isActive) {
  689. continue;
  690. }
  691. const start = bufferViewHeader.byteOffset;
  692. const end = start + bufferViewHeader.byteLength;
  693. const buffer = buffersU8[bufferViewHeader.buffer];
  694. const bufferView = buffer.subarray(start, end);
  695. bufferViewsU8[i] = bufferView;
  696. }
  697. return bufferViewsU8;
  698. }
  699. /**
  700. * Parse the three availability bitstreams and store them in the subtree
  701. *
  702. * @param {ImplicitSubtree} subtree The subtree to modify
  703. * @param {object} subtreeJson The subtree JSON
  704. * @param {ImplicitTileset} implicitTileset The implicit tileset this subtree belongs to
  705. * @param {object} bufferViewsU8 A dictionary of buffer view index to a Uint8Array of its contents.
  706. * @private
  707. */
  708. function parseAvailability(
  709. subtree,
  710. subtreeJson,
  711. implicitTileset,
  712. bufferViewsU8,
  713. ) {
  714. const branchingFactor = implicitTileset.branchingFactor;
  715. const subtreeLevels = implicitTileset.subtreeLevels;
  716. const tileAvailabilityBits =
  717. (Math.pow(branchingFactor, subtreeLevels) - 1) / (branchingFactor - 1);
  718. const childSubtreeBits = Math.pow(branchingFactor, subtreeLevels);
  719. // availableCount is only needed for the metadata jump buffer, which
  720. // corresponds to the tile availability bitstream.
  721. const hasMetadataExtension = hasExtension(subtreeJson, "3DTILES_metadata");
  722. const hasTileMetadata = defined(subtree._tilePropertyTableJson);
  723. let computeAvailableCountEnabled = hasMetadataExtension || hasTileMetadata;
  724. subtree._tileAvailability = parseAvailabilityBitstream(
  725. subtreeJson.tileAvailability,
  726. bufferViewsU8,
  727. tileAvailabilityBits,
  728. computeAvailableCountEnabled,
  729. );
  730. const hasContentMetadata = subtree._contentPropertyTableJsons.length > 0;
  731. computeAvailableCountEnabled =
  732. computeAvailableCountEnabled || hasContentMetadata;
  733. for (let i = 0; i < subtreeJson.contentAvailabilityHeaders.length; i++) {
  734. const bitstream = parseAvailabilityBitstream(
  735. subtreeJson.contentAvailabilityHeaders[i],
  736. bufferViewsU8,
  737. // content availability has the same length as tile availability.
  738. tileAvailabilityBits,
  739. computeAvailableCountEnabled,
  740. );
  741. subtree._contentAvailabilityBitstreams.push(bitstream);
  742. }
  743. subtree._childSubtreeAvailability = parseAvailabilityBitstream(
  744. subtreeJson.childSubtreeAvailability,
  745. bufferViewsU8,
  746. childSubtreeBits,
  747. );
  748. }
  749. /**
  750. * Given the JSON describing an availability bitstream, turn it into an
  751. * in-memory representation using an {@link ImplicitAvailabilityBitstream}
  752. * object. This handles both constants and bitstreams from a bufferView.
  753. *
  754. * @param {object} availabilityJson A JSON object representing the availability
  755. * @param {object} bufferViewsU8 A dictionary of bufferView index to its Uint8Array contents.
  756. * @param {number} lengthBits The length of the availability bitstream in bits
  757. * @param {boolean} [computeAvailableCountEnabled] If true and availabilityJson.availableCount is undefined, the availableCount will be computed.
  758. * @returns {ImplicitAvailabilityBitstream} The parsed bitstream object
  759. * @private
  760. */
  761. function parseAvailabilityBitstream(
  762. availabilityJson,
  763. bufferViewsU8,
  764. lengthBits,
  765. computeAvailableCountEnabled,
  766. ) {
  767. if (defined(availabilityJson.constant)) {
  768. return new ImplicitAvailabilityBitstream({
  769. constant: Boolean(availabilityJson.constant),
  770. lengthBits: lengthBits,
  771. availableCount: availabilityJson.availableCount,
  772. });
  773. }
  774. let bufferView;
  775. // Check for bitstream first, which is part of the current schema.
  776. // bufferView is the name of the bitstream from an older schema.
  777. if (defined(availabilityJson.bitstream)) {
  778. bufferView = bufferViewsU8[availabilityJson.bitstream];
  779. } else if (defined(availabilityJson.bufferView)) {
  780. bufferView = bufferViewsU8[availabilityJson.bufferView];
  781. }
  782. return new ImplicitAvailabilityBitstream({
  783. bitstream: bufferView,
  784. lengthBits: lengthBits,
  785. availableCount: availabilityJson.availableCount,
  786. computeAvailableCountEnabled: computeAvailableCountEnabled,
  787. });
  788. }
  789. /**
  790. * Parse the metadata table for the tile metadata, storing a {@link MetadataTable}
  791. * in the subtree.
  792. *
  793. * @param {ImplicitSubtree} subtree The subtree
  794. * @param {ImplicitTileset} implicitTileset The implicit tileset this subtree belongs to.
  795. * @param {object} bufferViewsU8 A dictionary of bufferView index to its Uint8Array contents.
  796. * @private
  797. */
  798. function parseTileMetadataTable(subtree, implicitTileset, bufferViewsU8) {
  799. const tilePropertyTableJson = subtree._tilePropertyTableJson;
  800. const tileCount = subtree._tileAvailability.availableCount;
  801. const metadataSchema = implicitTileset.metadataSchema;
  802. const tileMetadataClassName = tilePropertyTableJson.class;
  803. const tileMetadataClass = metadataSchema.classes[tileMetadataClassName];
  804. subtree._tileMetadataTable = new MetadataTable({
  805. class: tileMetadataClass,
  806. count: tileCount,
  807. properties: tilePropertyTableJson.properties,
  808. bufferViews: bufferViewsU8,
  809. });
  810. }
  811. /**
  812. * Parse the metadata tables for the content metadata, storing an array of
  813. * {@link MetadataTable}s in the subtree.
  814. *
  815. * @param {ImplicitSubtree} subtree The subtree
  816. * @param {ImplicitTileset} implicitTileset The implicit tileset this subtree belongs to.
  817. * @param {object} bufferViewsU8 A dictionary of bufferView index to its Uint8Array contents.
  818. * @private
  819. */
  820. function parseContentMetadataTables(subtree, implicitTileset, bufferViewsU8) {
  821. const contentPropertyTableJsons = subtree._contentPropertyTableJsons;
  822. const contentAvailabilityBitstreams = subtree._contentAvailabilityBitstreams;
  823. const metadataSchema = implicitTileset.metadataSchema;
  824. const contentMetadataTables = subtree._contentMetadataTables;
  825. for (let i = 0; i < contentPropertyTableJsons.length; i++) {
  826. const contentPropertyTableJson = contentPropertyTableJsons[i];
  827. const contentAvailabilityBitsteam = contentAvailabilityBitstreams[i];
  828. const contentCount = contentAvailabilityBitsteam.availableCount;
  829. const contentMetadataClassName = contentPropertyTableJson.class;
  830. const contentMetadataClass =
  831. metadataSchema.classes[contentMetadataClassName];
  832. const metadataTable = new MetadataTable({
  833. class: contentMetadataClass,
  834. count: contentCount,
  835. properties: contentPropertyTableJson.properties,
  836. bufferViews: bufferViewsU8,
  837. });
  838. contentMetadataTables.push(metadataTable);
  839. }
  840. }
  841. /**
  842. * Make a jump buffer, i.e. a map of a bit index to the metadata entity ID.
  843. * <p>
  844. * For unavailable tiles and content, the jump buffer entries will be uninitialized.
  845. * Use the tile and content availability to determine whether a jump buffer value is valid.
  846. * </p>
  847. *
  848. * @param {ImplicitAvailabilityBitstream} availability The availability bitstream to create the jump buffer from.
  849. * @returns {Array} The resulting jump buffer.
  850. * @private
  851. */
  852. function makeJumpBuffer(availability) {
  853. let entityId = 0;
  854. const bufferLength = availability.lengthBits;
  855. const availableCount = availability.availableCount;
  856. let jumpBuffer;
  857. if (availableCount < 256) {
  858. jumpBuffer = new Uint8Array(bufferLength);
  859. } else if (availableCount < 65536) {
  860. jumpBuffer = new Uint16Array(bufferLength);
  861. } else {
  862. jumpBuffer = new Uint32Array(bufferLength);
  863. }
  864. for (let i = 0; i < availability.lengthBits; i++) {
  865. if (availability.getBit(i)) {
  866. jumpBuffer[i] = entityId;
  867. entityId++;
  868. }
  869. }
  870. return jumpBuffer;
  871. }
  872. /**
  873. * Make the jump buffer, i.e. a map of a bit index to the metadata entity ID,
  874. * for the content metadata. This is stored in the subtree.
  875. *
  876. * @param {ImplicitSubtree} subtree The subtree
  877. * @private
  878. */
  879. function makeTileJumpBuffer(subtree) {
  880. const tileJumpBuffer = makeJumpBuffer(subtree._tileAvailability);
  881. subtree._tileJumpBuffer = tileJumpBuffer;
  882. }
  883. /**
  884. * Make the jump buffers, i.e. maps of bit indices to the metadata entity IDs,
  885. * for the content metadata. This is stored in the subtree.
  886. *
  887. * @param {ImplicitSubtree} subtree The subtree
  888. * @private
  889. */
  890. function makeContentJumpBuffers(subtree) {
  891. const contentJumpBuffers = subtree._contentJumpBuffers;
  892. const contentAvailabilityBitstreams = subtree._contentAvailabilityBitstreams;
  893. for (let i = 0; i < contentAvailabilityBitstreams.length; i++) {
  894. const contentAvailability = contentAvailabilityBitstreams[i];
  895. const contentJumpBuffer = makeJumpBuffer(contentAvailability);
  896. contentJumpBuffers.push(contentJumpBuffer);
  897. }
  898. }
  899. /**
  900. * Given the implicit tiling coordinates for a tile, get the index within the
  901. * subtree's tile availability bitstream.
  902. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a tile
  903. * @return {number} The tile's index within the subtree.
  904. * @private
  905. */
  906. ImplicitSubtree.prototype.getTileIndex = function (implicitCoordinates) {
  907. const localLevel =
  908. implicitCoordinates.level - this._implicitCoordinates.level;
  909. if (localLevel < 0 || this._subtreeLevels <= localLevel) {
  910. throw new RuntimeError("level is out of bounds for this subtree");
  911. }
  912. const subtreeCoordinates = implicitCoordinates.getSubtreeCoordinates();
  913. const offsetCoordinates =
  914. subtreeCoordinates.getOffsetCoordinates(implicitCoordinates);
  915. const index = offsetCoordinates.tileIndex;
  916. return index;
  917. };
  918. /**
  919. * Given the implicit tiling coordinates for a child subtree, get the index within the
  920. * subtree's child subtree availability bitstream.
  921. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a child subtree
  922. * @return {number} The child subtree's index within the subtree's child subtree availability bitstream.
  923. * @private
  924. */
  925. ImplicitSubtree.prototype.getChildSubtreeIndex = function (
  926. implicitCoordinates,
  927. ) {
  928. const localLevel =
  929. implicitCoordinates.level - this._implicitCoordinates.level;
  930. if (localLevel !== this._implicitCoordinates.subtreeLevels) {
  931. throw new RuntimeError("level is out of bounds for this subtree");
  932. }
  933. // Call getParentSubtreeCoordinates instead of getSubtreeCoordinates because the
  934. // child subtree is by definition the root of its own subtree, so we need to find
  935. // the parent subtree.
  936. const parentSubtreeCoordinates =
  937. implicitCoordinates.getParentSubtreeCoordinates();
  938. const offsetCoordinates =
  939. parentSubtreeCoordinates.getOffsetCoordinates(implicitCoordinates);
  940. const index = offsetCoordinates.mortonIndex;
  941. return index;
  942. };
  943. /**
  944. * Get the entity ID for a tile within this subtree.
  945. * @param {ImplicitSubtree} subtree The subtree
  946. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a tile
  947. * @return {number} The entity ID for this tile for accessing tile metadata, or <code>undefined</code> if not applicable.
  948. *
  949. * @private
  950. */
  951. function getTileEntityId(subtree, implicitCoordinates) {
  952. if (!defined(subtree._tileMetadataTable)) {
  953. return undefined;
  954. }
  955. const tileIndex = subtree.getTileIndex(implicitCoordinates);
  956. if (subtree._tileAvailability.getBit(tileIndex)) {
  957. return subtree._tileJumpBuffer[tileIndex];
  958. }
  959. return undefined;
  960. }
  961. /**
  962. * Get the entity ID for a content within this subtree.
  963. * @param {ImplicitSubtree} subtree The subtree
  964. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a content
  965. * @param {number} contentIndex The content index, for distinguishing between multiple contents.
  966. * @return {number} The entity ID for this content for accessing content metadata, or <code>undefined</code> if not applicable.
  967. *
  968. * @private
  969. */
  970. function getContentEntityId(subtree, implicitCoordinates, contentIndex) {
  971. const metadataTables = subtree._contentMetadataTables;
  972. if (!defined(metadataTables)) {
  973. return undefined;
  974. }
  975. const metadataTable = metadataTables[contentIndex];
  976. if (!defined(metadataTable)) {
  977. return undefined;
  978. }
  979. const availability = subtree._contentAvailabilityBitstreams[contentIndex];
  980. const tileIndex = subtree.getTileIndex(implicitCoordinates);
  981. if (availability.getBit(tileIndex)) {
  982. const contentJumpBuffer = subtree._contentJumpBuffers[contentIndex];
  983. return contentJumpBuffer[tileIndex];
  984. }
  985. return undefined;
  986. }
  987. /**
  988. * Create and return a metadata table view for a tile within this subtree.
  989. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a tile
  990. * @return {ImplicitMetadataView} The metadata view for this tile, or <code>undefined</code> if not applicable.
  991. *
  992. * @private
  993. */
  994. ImplicitSubtree.prototype.getTileMetadataView = function (implicitCoordinates) {
  995. const entityId = getTileEntityId(this, implicitCoordinates);
  996. if (!defined(entityId)) {
  997. return undefined;
  998. }
  999. const metadataTable = this._tileMetadataTable;
  1000. return new ImplicitMetadataView({
  1001. class: metadataTable.class,
  1002. metadataTable: metadataTable,
  1003. entityId: entityId,
  1004. propertyTableJson: this._tilePropertyTableJson,
  1005. });
  1006. };
  1007. /**
  1008. * Create and return a metadata table view for a content within this subtree.
  1009. * @param {ImplicitTileCoordinates} implicitCoordinates The global coordinates of a content
  1010. * @param {number} contentIndex The index of the content used to distinguish between multiple contents
  1011. * @return {ImplicitMetadataView} The metadata view for this content, or <code>undefined</code> if not applicable.
  1012. *
  1013. * @private
  1014. */
  1015. ImplicitSubtree.prototype.getContentMetadataView = function (
  1016. implicitCoordinates,
  1017. contentIndex,
  1018. ) {
  1019. const entityId = getContentEntityId(this, implicitCoordinates, contentIndex);
  1020. if (!defined(entityId)) {
  1021. return undefined;
  1022. }
  1023. const metadataTable = this._contentMetadataTables[contentIndex];
  1024. const propertyTableJson = this._contentPropertyTableJsons[contentIndex];
  1025. return new ImplicitMetadataView({
  1026. class: metadataTable.class,
  1027. metadataTable: metadataTable,
  1028. entityId: entityId,
  1029. contentIndex: contentIndex,
  1030. propertyTableJson: propertyTableJson,
  1031. });
  1032. };
  1033. /**
  1034. * @private
  1035. */
  1036. ImplicitSubtree.prototype.isDestroyed = function () {
  1037. return false;
  1038. };
  1039. /**
  1040. * @private
  1041. */
  1042. ImplicitSubtree.prototype.destroy = function () {
  1043. if (defined(this._bufferLoader)) {
  1044. ResourceCache.unload(this._bufferLoader);
  1045. }
  1046. return destroyObject(this);
  1047. };
  1048. export default ImplicitSubtree;