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

Cesium3DTilesVoxelProvider.js 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import Cesium3DTilesetMetadata from "./Cesium3DTilesetMetadata.js";
  3. import Check from "../Core/Check.js";
  4. import Frozen from "../Core/Frozen.js";
  5. import defined from "../Core/defined.js";
  6. import Ellipsoid from "../Core/Ellipsoid.js";
  7. import hasExtension from "./hasExtension.js";
  8. import ImplicitSubtree from "./ImplicitSubtree.js";
  9. import ImplicitSubtreeCache from "./ImplicitSubtreeCache.js";
  10. import ImplicitTileCoordinates from "./ImplicitTileCoordinates.js";
  11. import ImplicitTileset from "./ImplicitTileset.js";
  12. import Matrix3 from "../Core/Matrix3.js";
  13. import Matrix4 from "../Core/Matrix4.js";
  14. import MetadataSemantic from "./MetadataSemantic.js";
  15. import MetadataType from "./MetadataType.js";
  16. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  17. import preprocess3DTileContent from "./preprocess3DTileContent.js";
  18. import Resource from "../Core/Resource.js";
  19. import ResourceCache from "./ResourceCache.js";
  20. import RuntimeError from "../Core/RuntimeError.js";
  21. import VoxelContent from "./VoxelContent.js";
  22. import VoxelMetadataOrder from "./VoxelMetadataOrder.js";
  23. import VoxelShapeType from "./VoxelShapeType.js";
  24. import CesiumMath from "../Core/Math.js";
  25. import Quaternion from "../Core/Quaternion.js";
  26. /**
  27. * @typedef {object} Cesium3DTilesVoxelProvider.ConstructorOptions
  28. *
  29. * Initialization options for the Cesium3DTilesVoxelProvider constructor
  30. *
  31. * @property {string} className The class in the tileset schema describing voxel metadata.
  32. * @property {string[]} names The metadata names.
  33. * @property {MetadataType[]} types The metadata types.
  34. * @property {MetadataComponentType[]} componentTypes The metadata component types.
  35. * @property {VoxelShapeType} shape The {@link VoxelShapeType}.
  36. * @property {Cartesian3} dimensions The number of voxels per dimension of a tile. This is the same for all tiles in the dataset.
  37. * @property {Cartesian3} [paddingBefore=Cartesian3.ZERO] The number of padding voxels before the tile. This improves rendering quality when sampling the edge of a tile, but it increases memory usage.
  38. * @property {Cartesian3} [paddingAfter=Cartesian3.ZERO] The number of padding voxels after the tile. This improves rendering quality when sampling the edge of a tile, but it increases memory usage.
  39. * @property {Matrix4} [globalTransform=Matrix4.IDENTITY] A transform from local space to global space.
  40. * @property {Matrix4} [shapeTransform=Matrix4.IDENTITY] A transform from shape space to local space.
  41. * @property {Cartesian3} [minBounds] The minimum bounds.
  42. * @property {Cartesian3} [maxBounds] The maximum bounds.
  43. * @property {number[][]} [minimumValues] The metadata minimum values.
  44. * @property {number[][]} [maximumValues] The metadata maximum values.
  45. * @property {number} [maximumTileCount] The maximum number of tiles that exist for this provider. This value is used as a hint to the voxel renderer to allocate an appropriate amount of GPU memory. If this value is not known it can be undefined.
  46. */
  47. /**
  48. * A {@link VoxelProvider} that fetches voxel data from a 3D Tiles tileset.
  49. * <p>
  50. * Implements the {@link VoxelProvider} interface.
  51. * </p>
  52. * <div class="notice">
  53. * This object is normally not instantiated directly, use {@link Cesium3DTilesVoxelProvider.fromUrl}.
  54. * </div>
  55. *
  56. * @alias Cesium3DTilesVoxelProvider
  57. * @constructor
  58. * @augments VoxelProvider
  59. *
  60. * @param {Cesium3DTilesVoxelProvider.ConstructorOptions} options An object describing initialization options
  61. *
  62. * @see Cesium3DTilesVoxelProvider.fromUrl
  63. * @see VoxelProvider
  64. * @see VoxelPrimitive
  65. * @see VoxelShapeType
  66. *
  67. * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
  68. */
  69. function Cesium3DTilesVoxelProvider(options) {
  70. options = options ?? Frozen.EMPTY_OBJECT;
  71. const {
  72. className,
  73. names,
  74. types,
  75. componentTypes,
  76. shape,
  77. dimensions,
  78. paddingBefore = Cartesian3.ZERO.clone(),
  79. paddingAfter = Cartesian3.ZERO.clone(),
  80. globalTransform = Matrix4.IDENTITY.clone(),
  81. shapeTransform = Matrix4.IDENTITY.clone(),
  82. minBounds,
  83. maxBounds,
  84. minimumValues,
  85. maximumValues,
  86. maximumTileCount,
  87. } = options;
  88. //>>includeStart('debug', pragmas.debug);
  89. Check.typeOf.string("className", className);
  90. Check.typeOf.object("names", names);
  91. Check.typeOf.object("types", types);
  92. Check.typeOf.object("componentTypes", componentTypes);
  93. Check.typeOf.string("shape", shape);
  94. Check.typeOf.object("dimensions", dimensions);
  95. //>>includeEnd('debug');
  96. this._shapeTransform = shapeTransform;
  97. this._globalTransform = globalTransform;
  98. this._shape = shape;
  99. this._minBounds = minBounds;
  100. this._maxBounds = maxBounds;
  101. this._dimensions = dimensions;
  102. this._paddingBefore = paddingBefore;
  103. this._paddingAfter = paddingAfter;
  104. this._className = className;
  105. this._names = names;
  106. this._types = types;
  107. this._componentTypes = componentTypes;
  108. this._metadataOrder =
  109. shape === VoxelShapeType.ELLIPSOID
  110. ? VoxelMetadataOrder.Z_UP
  111. : VoxelMetadataOrder.Y_UP;
  112. this._minimumValues = minimumValues;
  113. this._maximumValues = maximumValues;
  114. this._maximumTileCount = maximumTileCount;
  115. this._availableLevels = undefined;
  116. this._implicitTileset = undefined;
  117. this._subtreeCache = new ImplicitSubtreeCache();
  118. }
  119. Object.defineProperties(Cesium3DTilesVoxelProvider.prototype, {
  120. /**
  121. * A transform from local space to global space.
  122. *
  123. * @memberof Cesium3DTilesVoxelProvider.prototype
  124. * @type {Matrix4}
  125. * @default Matrix4.IDENTITY
  126. * @readonly
  127. */
  128. globalTransform: {
  129. get: function () {
  130. return this._globalTransform;
  131. },
  132. },
  133. /**
  134. * A transform from shape space to local space.
  135. *
  136. * @memberof Cesium3DTilesVoxelProvider.prototype
  137. * @type {Matrix4}
  138. * @default Matrix4.IDENTITY
  139. * @readonly
  140. */
  141. shapeTransform: {
  142. get: function () {
  143. return this._shapeTransform;
  144. },
  145. },
  146. /**
  147. * Gets the {@link VoxelShapeType}
  148. *
  149. * @memberof Cesium3DTilesVoxelProvider.prototype
  150. * @type {VoxelShapeType}
  151. * @readonly
  152. */
  153. shape: {
  154. get: function () {
  155. return this._shape;
  156. },
  157. },
  158. /**
  159. * Gets the minimum bounds.
  160. * If undefined, the shape's default minimum bounds will be used instead.
  161. *
  162. * @memberof Cesium3DTilesVoxelProvider.prototype
  163. * @type {Cartesian3|undefined}
  164. * @readonly
  165. */
  166. minBounds: {
  167. get: function () {
  168. return this._minBounds;
  169. },
  170. },
  171. /**
  172. * Gets the maximum bounds.
  173. * If undefined, the shape's default maximum bounds will be used instead.
  174. *
  175. * @memberof Cesium3DTilesVoxelProvider.prototype
  176. * @type {Cartesian3|undefined}
  177. * @readonly
  178. */
  179. maxBounds: {
  180. get: function () {
  181. return this._maxBounds;
  182. },
  183. },
  184. /**
  185. * Gets the number of voxels per dimension of a tile. This is the same for all tiles in the dataset.
  186. *
  187. * @memberof Cesium3DTilesVoxelProvider.prototype
  188. * @type {Cartesian3}
  189. * @readonly
  190. */
  191. dimensions: {
  192. get: function () {
  193. return this._dimensions;
  194. },
  195. },
  196. /**
  197. * Gets the number of padding voxels before the tile. This improves rendering quality when sampling the edge of a tile, but it increases memory usage.
  198. *
  199. * @memberof Cesium3DTilesVoxelProvider.prototype
  200. * @type {Cartesian3}
  201. * @default Cartesian3.ZERO
  202. * @readonly
  203. */
  204. paddingBefore: {
  205. get: function () {
  206. return this._paddingBefore;
  207. },
  208. },
  209. /**
  210. * Gets the number of padding voxels after the tile. This improves rendering quality when sampling the edge of a tile, but it increases memory usage.
  211. *
  212. * @memberof Cesium3DTilesVoxelProvider.prototype
  213. * @type {Cartesian3}
  214. * @default Cartesian3.ZERO
  215. * @readonly
  216. */
  217. paddingAfter: {
  218. get: function () {
  219. return this._paddingAfter;
  220. },
  221. },
  222. /**
  223. * The metadata class for this tileset.
  224. *
  225. * @memberof Cesium3DTilesVoxelProvider.prototype
  226. * @type {string}
  227. * @readonly
  228. */
  229. className: {
  230. get: function () {
  231. return this._className;
  232. },
  233. },
  234. /**
  235. * Gets the metadata names.
  236. *
  237. * @memberof Cesium3DTilesVoxelProvider.prototype
  238. * @type {string[]}
  239. * @readonly
  240. */
  241. names: {
  242. get: function () {
  243. return this._names;
  244. },
  245. },
  246. /**
  247. * Gets the metadata types.
  248. *
  249. * @memberof Cesium3DTilesVoxelProvider.prototype
  250. * @type {MetadataType[]}
  251. * @readonly
  252. */
  253. types: {
  254. get: function () {
  255. return this._types;
  256. },
  257. },
  258. /**
  259. * Gets the metadata component types.
  260. *
  261. * @memberof Cesium3DTilesVoxelProvider.prototype
  262. * @type {MetadataComponentType[]}
  263. * @readonly
  264. */
  265. componentTypes: {
  266. get: function () {
  267. return this._componentTypes;
  268. },
  269. },
  270. /**
  271. * Gets the ordering of the metadata in the buffers.
  272. *
  273. * @memberof Cesium3DTilesVoxelProvider.prototype
  274. * @type {VoxelMetadataOrder}
  275. * @readonly
  276. * @private
  277. */
  278. metadataOrder: {
  279. get: function () {
  280. return this._metadataOrder;
  281. },
  282. },
  283. /**
  284. * Gets the metadata minimum values.
  285. *
  286. * @memberof Cesium3DTilesVoxelProvider.prototype
  287. * @type {number[][]|undefined}
  288. * @readonly
  289. */
  290. minimumValues: {
  291. get: function () {
  292. return this._minimumValues;
  293. },
  294. },
  295. /**
  296. * Gets the metadata maximum values.
  297. *
  298. * @memberof Cesium3DTilesVoxelProvider.prototype
  299. * @type {number[][]|undefined}
  300. * @readonly
  301. */
  302. maximumValues: {
  303. get: function () {
  304. return this._maximumValues;
  305. },
  306. },
  307. /**
  308. * The maximum number of tiles that exist for this provider.
  309. * This value is used as a hint to the voxel renderer to allocate an appropriate amount of GPU memory.
  310. * If this value is not known it can be undefined.
  311. *
  312. * @memberof Cesium3DTilesVoxelProvider.prototype
  313. * @type {number|undefined}
  314. * @readonly
  315. */
  316. maximumTileCount: {
  317. get: function () {
  318. return this._maximumTileCount;
  319. },
  320. },
  321. /**
  322. * The number of levels of detail containing available tiles in the tileset.
  323. *
  324. * @memberof Cesium3DTilesVoxelProvider.prototype
  325. * @type {number|undefined}
  326. * @readonly
  327. */
  328. availableLevels: {
  329. get: function () {
  330. return this._availableLevels;
  331. },
  332. },
  333. });
  334. /**
  335. * Creates a {@link Cesium3DTilesVoxelProvider} that fetches voxel data from a 3D Tiles tileset.
  336. *
  337. * @param {Resource|string} url The URL to a tileset JSON file
  338. * @returns {Promise<Cesium3DTilesVoxelProvider>} The created provider
  339. *
  340. * @exception {RuntimeException} Root must have content
  341. * @exception {RuntimeException} Root tile content must have 3DTILES_content_voxels extension
  342. * @exception {RuntimeException} Root tile must have implicit tiling
  343. * @exception {RuntimeException} Tileset must have a metadata schema
  344. * @exception {RuntimeException} Only box, region and 3DTILES_bounding_volume_cylinder are supported in Cesium3DTilesVoxelProvider
  345. *
  346. * @example
  347. * try {
  348. * const voxelProvider = await Cesium3DTilesVoxelProvider.fromUrl(
  349. * "http://localhost:8002/tilesets/voxel/tileset.json"
  350. * );
  351. * const voxelPrimitive = new VoxelPrimitive({
  352. * provider: voxelProvider,
  353. * customShader: customShader,
  354. * });
  355. * scene.primitives.add(voxelPrimitive);
  356. * } catch (error) {
  357. * console.error(`Error creating voxel primitive: ${error}`);
  358. * }
  359. *
  360. * @see {@link VoxelPrimitive}
  361. */
  362. Cesium3DTilesVoxelProvider.fromUrl = async function (url) {
  363. //>>includeStart('debug', pragmas.debug);
  364. Check.defined("url", url);
  365. //>>includeEnd('debug');
  366. const resource = Resource.createIfNeeded(url);
  367. const tilesetJson = await resource.fetchJson();
  368. validate(tilesetJson);
  369. const schemaLoader = getMetadataSchemaLoader(tilesetJson, resource);
  370. await schemaLoader.load();
  371. const { root } = tilesetJson;
  372. const metadataJson = hasExtension(tilesetJson, "3DTILES_metadata")
  373. ? tilesetJson.extensions["3DTILES_metadata"]
  374. : tilesetJson;
  375. const tilesetMetadata = new Cesium3DTilesetMetadata({
  376. metadataJson: metadataJson,
  377. schema: schemaLoader.schema,
  378. });
  379. const voxel = root.content.extensions["3DTILES_content_voxels"];
  380. const className = voxel.class;
  381. const providerOptions = getAttributeInfo(tilesetMetadata, className);
  382. Object.assign(providerOptions, getShape(root));
  383. if (defined(root.transform)) {
  384. providerOptions.globalTransform = Matrix4.unpack(root.transform);
  385. } else {
  386. providerOptions.globalTransform = Matrix4.clone(Matrix4.IDENTITY);
  387. }
  388. providerOptions.dimensions = Cartesian3.unpack(voxel.dimensions);
  389. providerOptions.maximumTileCount = getTileCount(tilesetMetadata);
  390. if (defined(voxel.padding)) {
  391. providerOptions.paddingBefore = Cartesian3.unpack(voxel.padding.before);
  392. providerOptions.paddingAfter = Cartesian3.unpack(voxel.padding.after);
  393. }
  394. const provider = new Cesium3DTilesVoxelProvider(providerOptions);
  395. const implicitTileset = new ImplicitTileset(
  396. resource,
  397. root,
  398. schemaLoader.schema,
  399. );
  400. provider._implicitTileset = implicitTileset;
  401. provider._availableLevels = implicitTileset.availableLevels;
  402. ResourceCache.unload(schemaLoader);
  403. return provider;
  404. };
  405. function getTileCount(metadata) {
  406. if (!defined(metadata.tileset)) {
  407. return undefined;
  408. }
  409. return metadata.tileset.getPropertyBySemantic(
  410. MetadataSemantic.TILESET_TILE_COUNT,
  411. );
  412. }
  413. function validate(tileset) {
  414. const root = tileset.root;
  415. if (!defined(root.content)) {
  416. throw new RuntimeError("Root must have content");
  417. }
  418. if (!hasExtension(root.content, "3DTILES_content_voxels")) {
  419. throw new RuntimeError(
  420. "Root tile content must have 3DTILES_content_voxels extension",
  421. );
  422. }
  423. if (
  424. !hasExtension(root, "3DTILES_implicit_tiling") &&
  425. !defined(root.implicitTiling)
  426. ) {
  427. throw new RuntimeError("Root tile must have implicit tiling");
  428. }
  429. if (
  430. !defined(tileset.schema) &&
  431. !defined(tileset.schemaUri) &&
  432. !hasExtension(tileset, "3DTILES_metadata")
  433. ) {
  434. throw new RuntimeError("Tileset must have a metadata schema");
  435. }
  436. }
  437. function getShape(tile) {
  438. const boundingVolume = tile.boundingVolume;
  439. if (defined(boundingVolume.box)) {
  440. return getBoxShape(boundingVolume.box);
  441. } else if (defined(boundingVolume.region)) {
  442. return getEllipsoidShape(boundingVolume.region);
  443. } else if (hasExtension(boundingVolume, "3DTILES_bounding_volume_cylinder")) {
  444. return getCylinderShape(
  445. boundingVolume.extensions["3DTILES_bounding_volume_cylinder"],
  446. );
  447. }
  448. throw new RuntimeError(
  449. "Only box, region and 3DTILES_bounding_volume_cylinder are supported in Cesium3DTilesVoxelProvider",
  450. );
  451. }
  452. function getEllipsoidShape(region) {
  453. const west = region[0];
  454. const south = region[1];
  455. const east = region[2];
  456. const north = region[3];
  457. const minHeight = region[4];
  458. const maxHeight = region[5];
  459. const shapeTransform = Matrix4.fromScale(Ellipsoid.WGS84.radii);
  460. const minBounds = new Cartesian3(west, south, minHeight);
  461. const maxBounds = new Cartesian3(east, north, maxHeight);
  462. return {
  463. shape: VoxelShapeType.ELLIPSOID,
  464. minBounds: minBounds,
  465. maxBounds: maxBounds,
  466. shapeTransform: shapeTransform,
  467. };
  468. }
  469. const scratchScale = new Cartesian3();
  470. const scratchRotation = new Matrix3();
  471. function getBoxShape(box) {
  472. const obb = OrientedBoundingBox.unpack(box);
  473. const scale = Matrix3.getScale(obb.halfAxes, scratchScale);
  474. const rotation = Matrix3.getRotation(obb.halfAxes, scratchRotation);
  475. return {
  476. shape: VoxelShapeType.BOX,
  477. minBounds: Cartesian3.negate(scale, new Cartesian3()),
  478. maxBounds: Cartesian3.clone(scale),
  479. shapeTransform: Matrix4.fromRotationTranslation(rotation, obb.center),
  480. };
  481. }
  482. function getCylinderShape(cylinder) {
  483. const {
  484. minRadius,
  485. maxRadius,
  486. height,
  487. minAngle = -CesiumMath.PI,
  488. maxAngle = CesiumMath.PI,
  489. translation = [0, 0, 0],
  490. rotation = [0, 0, 0, 1],
  491. } = cylinder;
  492. //>>includeStart('debug', pragmas.debug);
  493. Check.typeOf.number("minRadius", minRadius);
  494. Check.typeOf.number("maxRadius", maxRadius);
  495. Check.typeOf.number("height", height);
  496. Check.typeOf.number("minAngle", minAngle);
  497. Check.typeOf.number("maxAngle", maxAngle);
  498. Check.typeOf.object("translation", translation);
  499. Check.typeOf.object("rotation", rotation);
  500. //>>includeEnd('debug');
  501. const minHeight = -0.5 * height + translation[2];
  502. const maxHeight = 0.5 * height + translation[2];
  503. const shapeTransform = Matrix4.fromTranslationQuaternionRotationScale(
  504. Cartesian3.unpack(translation),
  505. Quaternion.unpack(rotation),
  506. Cartesian3.ONE,
  507. );
  508. return {
  509. shape: VoxelShapeType.CYLINDER,
  510. minBounds: Cartesian3.fromElements(minRadius, minAngle, minHeight),
  511. maxBounds: Cartesian3.fromElements(maxRadius, maxAngle, maxHeight),
  512. shapeTransform: shapeTransform,
  513. };
  514. }
  515. function getMetadataSchemaLoader(tilesetJson, resource) {
  516. const { schemaUri, schema } = tilesetJson;
  517. if (!defined(schemaUri)) {
  518. return ResourceCache.getSchemaLoader({ schema });
  519. }
  520. return ResourceCache.getSchemaLoader({
  521. resource: resource.getDerivedResource({
  522. url: schemaUri,
  523. }),
  524. });
  525. }
  526. function getAttributeInfo(metadata, className) {
  527. const { schema, statistics } = metadata;
  528. const classStatistics = statistics?.classes[className];
  529. const properties = schema.classes[className].properties;
  530. const propertyInfo = Object.entries(properties).map(([id, property]) => {
  531. const { type, componentType } = property;
  532. const min = classStatistics?.properties[id].min;
  533. const max = classStatistics?.properties[id].max;
  534. const componentCount = MetadataType.getComponentCount(type);
  535. const minValue = copyArray(min, componentCount);
  536. const maxValue = copyArray(max, componentCount);
  537. return { id, type, componentType, minValue, maxValue };
  538. });
  539. const names = propertyInfo.map((info) => info.id);
  540. const types = propertyInfo.map((info) => info.type);
  541. const componentTypes = propertyInfo.map((info) => info.componentType);
  542. const minimumValues = propertyInfo.map((info) => info.minValue);
  543. const maximumValues = propertyInfo.map((info) => info.maxValue);
  544. const hasMinimumValues = minimumValues.some(defined);
  545. return {
  546. className,
  547. names,
  548. types,
  549. componentTypes,
  550. minimumValues: hasMinimumValues ? minimumValues : undefined,
  551. maximumValues: hasMinimumValues ? maximumValues : undefined,
  552. };
  553. }
  554. function copyArray(values, length) {
  555. // Copy input values into a new array of a specified length.
  556. // If the input is not an array, its value will be copied into the first element
  557. // of the returned array. If the input is an array shorter than the returned
  558. // array, the extra elements in the returned array will be undefined. If the
  559. // input is undefined, the return will be undefined.
  560. if (!defined(values)) {
  561. return;
  562. }
  563. const valuesArray = Array.isArray(values) ? values : [values];
  564. return Array.from({ length }, (v, i) => valuesArray[i]);
  565. }
  566. /**
  567. * Get the subtree at a given subtree coordinate
  568. * @param {VoxelProvider} provider The voxel provider
  569. * @param {ImplicitTileCoordinates} subtreeCoord The coordinate at which to retrieve the subtree
  570. * @returns {Promise<ImplicitSubtree>} The subtree at the given coordinate
  571. * @private
  572. */
  573. async function getSubtree(provider, subtreeCoord) {
  574. const implicitTileset = provider._implicitTileset;
  575. const subtreeCache = provider._subtreeCache;
  576. // First load the subtree to check if the tile is available.
  577. // If the subtree has been requested previously it might still be in the cache
  578. let subtree = subtreeCache.find(subtreeCoord);
  579. if (defined(subtree)) {
  580. return subtree;
  581. }
  582. const subtreeRelative = implicitTileset.subtreeUriTemplate.getDerivedResource(
  583. {
  584. templateValues: subtreeCoord.getTemplateValues(),
  585. },
  586. );
  587. const subtreeResource = implicitTileset.baseResource.getDerivedResource({
  588. url: subtreeRelative.url,
  589. });
  590. const arrayBuffer = await subtreeResource.fetchArrayBuffer();
  591. // Check one more time if the subtree is in the cache.
  592. // This could happen if there are two in-flight tile requests from the same
  593. // subtree and one finishes before the other.
  594. subtree = subtreeCache.find(subtreeCoord);
  595. if (defined(subtree)) {
  596. return subtree;
  597. }
  598. const preprocessed = preprocess3DTileContent(arrayBuffer);
  599. subtree = await ImplicitSubtree.fromSubtreeJson(
  600. subtreeResource,
  601. preprocessed.jsonPayload,
  602. preprocessed.binaryPayload,
  603. implicitTileset,
  604. subtreeCoord,
  605. );
  606. subtreeCache.addSubtree(subtree);
  607. return subtree;
  608. }
  609. /**
  610. * Requests the data for a given tile.
  611. *
  612. * @param {object} [options] Object with the following properties:
  613. * @param {number} [options.tileLevel=0] The tile's level.
  614. * @param {number} [options.tileX=0] The tile's X coordinate.
  615. * @param {number} [options.tileY=0] The tile's Y coordinate.
  616. * @param {number} [options.tileZ=0] The tile's Z coordinate.
  617. * @privateparam {number} [options.keyframe=0] The requested keyframe.
  618. * @returns {Promise<VoxelContent>|undefined} A promise resolving to a VoxelContent containing the data for the tile, or undefined if the request could not be scheduled this frame.
  619. */
  620. Cesium3DTilesVoxelProvider.prototype.requestData = async function (options) {
  621. options = options ?? Frozen.EMPTY_OBJECT;
  622. const {
  623. tileLevel = 0,
  624. tileX = 0,
  625. tileY = 0,
  626. tileZ = 0,
  627. keyframe = 0,
  628. } = options;
  629. if (keyframe !== 0) {
  630. return Promise.reject(
  631. `3D Tiles currently doesn't support time-dynamic data.`,
  632. );
  633. }
  634. // 1. Load the subtree that the tile belongs to (possibly from the subtree cache)
  635. // 2. Load the voxel content if available
  636. // Can't use a scratch variable here because the object is used inside the promise chain.
  637. const implicitTileset = this._implicitTileset;
  638. const tileCoordinates = new ImplicitTileCoordinates({
  639. subdivisionScheme: implicitTileset.subdivisionScheme,
  640. subtreeLevels: implicitTileset.subtreeLevels,
  641. level: tileLevel,
  642. x: tileX,
  643. y: tileY,
  644. z: tileZ,
  645. });
  646. // Find the coordinates of the parent subtree containing tileCoordinates
  647. // If tileCoordinates is a subtree child, use that subtree
  648. // If tileCoordinates is a subtree root, use its parent subtree
  649. const isSubtreeRoot =
  650. tileCoordinates.isSubtreeRoot() && tileCoordinates.level > 0;
  651. const subtreeCoord = isSubtreeRoot
  652. ? tileCoordinates.getParentSubtreeCoordinates()
  653. : tileCoordinates.getSubtreeCoordinates();
  654. const that = this;
  655. const subtree = await getSubtree(that, subtreeCoord);
  656. // NOTE: these two subtree methods are ONLY used by voxels!
  657. const isAvailable = isSubtreeRoot
  658. ? subtree.childSubtreeIsAvailableAtCoordinates
  659. : subtree.tileIsAvailableAtCoordinates;
  660. const available = isAvailable.call(subtree, tileCoordinates);
  661. if (!available) {
  662. return Promise.reject(
  663. `Tile is not available at level ${tileLevel}, x ${tileX}, y ${tileY}, z ${tileZ}.`,
  664. );
  665. }
  666. const { contentUriTemplates, baseResource } = implicitTileset;
  667. const gltfRelative = contentUriTemplates[0].getDerivedResource({
  668. templateValues: tileCoordinates.getTemplateValues(),
  669. });
  670. const gltfResource = baseResource.getDerivedResource({
  671. url: gltfRelative.url,
  672. });
  673. return VoxelContent.fromGltf(gltfResource);
  674. };
  675. export default Cesium3DTilesVoxelProvider;