I3SNode.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. import Cartographic from "../Core/Cartographic.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defined from "../Core/defined.js";
  4. import Ellipsoid from "../Core/Ellipsoid.js";
  5. import HeadingPitchRoll from "../Core/HeadingPitchRoll.js";
  6. import CesiumMath from "../Core/Math.js";
  7. import Matrix3 from "../Core/Matrix3.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import Resource from "../Core/Resource.js";
  10. import Quaternion from "../Core/Quaternion.js";
  11. import Transforms from "../Core/Transforms.js";
  12. import Cesium3DTile from "./Cesium3DTile.js";
  13. import I3SDataProvider from "./I3SDataProvider.js";
  14. import I3SFeature from "./I3SFeature.js";
  15. import I3SField from "./I3SField.js";
  16. import I3SGeometry from "./I3SGeometry.js";
  17. /**
  18. * This class implements an I3S Node. In CesiumJS each I3SNode creates a Cesium3DTile.
  19. * <p>
  20. * Do not construct this directly, instead access tiles through {@link I3SLayer}.
  21. * </p>
  22. * @alias I3SNode
  23. * @internalConstructor
  24. */
  25. function I3SNode(parent, ref, isRoot) {
  26. let level;
  27. let layer;
  28. let nodeIndex;
  29. let resource;
  30. if (isRoot) {
  31. level = 0;
  32. layer = parent;
  33. } else {
  34. level = parent._level + 1;
  35. layer = parent._layer;
  36. }
  37. if (typeof ref === "number") {
  38. nodeIndex = ref;
  39. } else {
  40. resource = parent.resource.getDerivedResource({
  41. url: `${ref}/`,
  42. });
  43. }
  44. this._parent = parent;
  45. this._dataProvider = parent._dataProvider;
  46. this._isRoot = isRoot;
  47. this._level = level;
  48. this._layer = layer;
  49. this._nodeIndex = nodeIndex;
  50. this._resource = resource;
  51. this._isLoading = false;
  52. this._tile = undefined;
  53. this._data = undefined;
  54. this._geometryData = [];
  55. this._featureData = [];
  56. this._fields = {};
  57. this._children = [];
  58. this._childrenReadyPromise = undefined;
  59. this._globalTransform = undefined;
  60. this._inverseGlobalTransform = undefined;
  61. this._inverseRotationMatrix = undefined;
  62. }
  63. Object.defineProperties(I3SNode.prototype, {
  64. /**
  65. * Gets the resource for the node.
  66. * @memberof I3SNode.prototype
  67. * @type {Resource}
  68. * @readonly
  69. */
  70. resource: {
  71. get: function () {
  72. return this._resource;
  73. },
  74. },
  75. /**
  76. * Gets the parent layer.
  77. * @memberof I3SNode.prototype
  78. * @type {I3SLayer}
  79. * @readonly
  80. */
  81. layer: {
  82. get: function () {
  83. return this._layer;
  84. },
  85. },
  86. /**
  87. * Gets the parent node.
  88. * @memberof I3SNode.prototype
  89. * @type {I3SNode|undefined}
  90. * @readonly
  91. */
  92. parent: {
  93. get: function () {
  94. return this._parent;
  95. },
  96. },
  97. /**
  98. * Gets the children nodes.
  99. * @memberof I3SNode.prototype
  100. * @type {I3SNode[]}
  101. * @readonly
  102. */
  103. children: {
  104. get: function () {
  105. return this._children;
  106. },
  107. },
  108. /**
  109. * Gets the collection of geometries.
  110. * @memberof I3SNode.prototype
  111. * @type {I3SGeometry[]}
  112. * @readonly
  113. */
  114. geometryData: {
  115. get: function () {
  116. return this._geometryData;
  117. },
  118. },
  119. /**
  120. * Gets the collection of features.
  121. * @memberof I3SNode.prototype
  122. * @type {I3SFeature[]}
  123. * @readonly
  124. */
  125. featureData: {
  126. get: function () {
  127. return this._featureData;
  128. },
  129. },
  130. /**
  131. * Gets the collection of fields.
  132. * @memberof I3SNode.prototype
  133. * @type {I3SField[]}
  134. * @readonly
  135. */
  136. fields: {
  137. get: function () {
  138. return this._fields;
  139. },
  140. },
  141. /**
  142. * Gets the Cesium3DTile for this node.
  143. * @memberof I3SNode.prototype
  144. * @type {Cesium3DTile}
  145. * @readonly
  146. */
  147. tile: {
  148. get: function () {
  149. return this._tile;
  150. },
  151. },
  152. /**
  153. * Gets the I3S data for this object.
  154. * @memberof I3SNode.prototype
  155. * @type {object}
  156. * @readonly
  157. */
  158. data: {
  159. get: function () {
  160. return this._data;
  161. },
  162. },
  163. });
  164. /**
  165. * @private
  166. */
  167. I3SNode.prototype.load = async function () {
  168. const that = this;
  169. function processData() {
  170. if (!that._isRoot) {
  171. // Create a new tile
  172. const tileDefinition = that._create3DTileDefinition();
  173. that._tile = new Cesium3DTile(
  174. that._layer._tileset,
  175. that._dataProvider.resource,
  176. tileDefinition,
  177. that._parent._tile
  178. );
  179. that._tile._i3sNode = that;
  180. }
  181. }
  182. // If we don't have a nodepage index load from json
  183. if (!defined(this._nodeIndex)) {
  184. const data = await I3SDataProvider.loadJson(
  185. this._resource,
  186. this._dataProvider._traceFetches
  187. );
  188. that._data = data;
  189. processData();
  190. return;
  191. }
  192. const node = await this._layer._getNodeInNodePages(this._nodeIndex);
  193. that._data = node;
  194. let uri;
  195. if (that._isRoot) {
  196. uri = "nodes/root/";
  197. } else if (defined(node.mesh)) {
  198. const uriIndex = node.mesh.geometry.resource;
  199. uri = `../${uriIndex}/`;
  200. }
  201. if (defined(uri)) {
  202. that._resource = that._parent.resource.getDerivedResource({ url: uri });
  203. }
  204. processData();
  205. };
  206. /**
  207. * Loads the node fields.
  208. * @returns {Promise<void>} A promise that is resolved when the I3S Node fields are loaded
  209. */
  210. I3SNode.prototype.loadFields = function () {
  211. // Check if we must load fields
  212. const fields = this._layer._data.attributeStorageInfo;
  213. const that = this;
  214. function createAndLoadField(fields, index) {
  215. const newField = new I3SField(that, fields[index]);
  216. that._fields[newField._storageInfo.name] = newField;
  217. return newField.load();
  218. }
  219. const promises = [];
  220. if (defined(fields)) {
  221. for (let i = 0; i < fields.length; i++) {
  222. promises.push(createAndLoadField(fields, i));
  223. }
  224. }
  225. return Promise.all(promises);
  226. };
  227. /**
  228. * Returns the fields for a given picked position
  229. * @param {Cartesian3} pickedPosition The picked position
  230. * @returns {object} Object containing field names and their values
  231. */
  232. I3SNode.prototype.getFieldsForPickedPosition = function (pickedPosition) {
  233. const geometry = this.geometryData[0];
  234. if (!defined(geometry.customAttributes.featureIndex)) {
  235. return {};
  236. }
  237. const location = geometry.getClosestPointIndexOnTriangle(
  238. pickedPosition.x,
  239. pickedPosition.y,
  240. pickedPosition.z
  241. );
  242. if (
  243. location.index === -1 ||
  244. location.index > geometry.customAttributes.featureIndex.length
  245. ) {
  246. return {};
  247. }
  248. const featureIndex = geometry.customAttributes.featureIndex[location.index];
  249. return this.getFieldsForFeature(featureIndex);
  250. };
  251. /**
  252. * Returns the fields for a given feature
  253. * @param {number} featureIndex Index of the feature whose attributes we want to get
  254. * @returns {object} Object containing field names and their values
  255. */
  256. I3SNode.prototype.getFieldsForFeature = function (featureIndex) {
  257. const featureFields = {};
  258. for (const fieldName in this.fields) {
  259. if (this.fields.hasOwnProperty(fieldName)) {
  260. const field = this.fields[fieldName];
  261. if (featureIndex >= 0 && featureIndex < field.values.length) {
  262. featureFields[field.name] = field.values[featureIndex];
  263. }
  264. }
  265. }
  266. return featureFields;
  267. };
  268. /**
  269. * @private
  270. */
  271. I3SNode.prototype._loadChildren = function () {
  272. const that = this;
  273. // If the promise for loading the children was already created, just return it
  274. if (defined(this._childrenReadyPromise)) {
  275. return this._childrenReadyPromise;
  276. }
  277. const childPromises = [];
  278. if (defined(that._data.children)) {
  279. for (
  280. let childIndex = 0;
  281. childIndex < that._data.children.length;
  282. childIndex++
  283. ) {
  284. const child = that._data.children[childIndex];
  285. const newChild = new I3SNode(
  286. that,
  287. defaultValue(child.href, child),
  288. false
  289. );
  290. that._children.push(newChild);
  291. childPromises.push(newChild.load());
  292. }
  293. }
  294. this._childrenReadyPromise = Promise.all(childPromises).then(function () {
  295. for (let i = 0; i < that._children.length; i++) {
  296. that._tile.children.push(that._children[i]._tile);
  297. }
  298. });
  299. return this._childrenReadyPromise;
  300. };
  301. /**
  302. * @private
  303. */
  304. I3SNode.prototype._loadGeometryData = function () {
  305. const geometryPromises = [];
  306. // To debug decoding for a specific tile, add a condition
  307. // that wraps this if/else to match the tile uri
  308. if (defined(this._data.geometryData)) {
  309. for (
  310. let geomIndex = 0;
  311. geomIndex < this._data.geometryData.length;
  312. geomIndex++
  313. ) {
  314. const curGeometryData = new I3SGeometry(
  315. this,
  316. this._data.geometryData[geomIndex].href
  317. );
  318. this._geometryData.push(curGeometryData);
  319. geometryPromises.push(curGeometryData.load());
  320. }
  321. } else if (defined(this._data.mesh)) {
  322. const geometryDefinition = this._layer._findBestGeometryBuffers(
  323. this._data.mesh.geometry.definition,
  324. ["position", "uv0"]
  325. );
  326. const geometryURI = `./geometries/${geometryDefinition.bufferIndex}/`;
  327. const newGeometryData = new I3SGeometry(this, geometryURI);
  328. newGeometryData._geometryDefinitions = geometryDefinition.definition;
  329. newGeometryData._geometryBufferInfo = geometryDefinition.geometryBufferInfo;
  330. this._geometryData.push(newGeometryData);
  331. geometryPromises.push(newGeometryData.load());
  332. }
  333. return Promise.all(geometryPromises);
  334. };
  335. /**
  336. * @private
  337. */
  338. I3SNode.prototype._loadFeatureData = function () {
  339. const featurePromises = [];
  340. // To debug decoding for a specific tile, add a condition
  341. // that wraps this if/else to match the tile uri
  342. if (defined(this._data.featureData)) {
  343. for (
  344. let featureIndex = 0;
  345. featureIndex < this._data.featureData.length;
  346. featureIndex++
  347. ) {
  348. const newFeatureData = new I3SFeature(
  349. this,
  350. this._data.featureData[featureIndex].href
  351. );
  352. this._featureData.push(newFeatureData);
  353. featurePromises.push(newFeatureData.load());
  354. }
  355. }
  356. return Promise.all(featurePromises);
  357. };
  358. /**
  359. * @private
  360. */
  361. I3SNode.prototype._clearGeometryData = function () {
  362. this._geometryData = [];
  363. };
  364. /**
  365. * @private
  366. */
  367. I3SNode.prototype._create3DTileDefinition = function () {
  368. const obb = this._data.obb;
  369. const mbs = this._data.mbs;
  370. if (!defined(obb) && !defined(mbs)) {
  371. console.error("Failed to load I3S node. Bounding volume is required.");
  372. return undefined;
  373. }
  374. let geoPosition;
  375. if (defined(obb)) {
  376. geoPosition = Cartographic.fromDegrees(
  377. obb.center[0],
  378. obb.center[1],
  379. obb.center[2]
  380. );
  381. } else {
  382. geoPosition = Cartographic.fromDegrees(mbs[0], mbs[1], mbs[2]);
  383. }
  384. // Offset bounding box position if we have a geoid service defined
  385. if (defined(this._dataProvider._geoidDataList) && defined(geoPosition)) {
  386. for (let i = 0; i < this._dataProvider._geoidDataList.length; i++) {
  387. const tile = this._dataProvider._geoidDataList[i];
  388. const projectedPos = tile.projection.project(geoPosition);
  389. if (
  390. projectedPos.x > tile.nativeExtent.west &&
  391. projectedPos.x < tile.nativeExtent.east &&
  392. projectedPos.y > tile.nativeExtent.south &&
  393. projectedPos.y < tile.nativeExtent.north
  394. ) {
  395. geoPosition.height += sampleGeoid(projectedPos.x, projectedPos.y, tile);
  396. break;
  397. }
  398. }
  399. }
  400. let boundingVolume = {};
  401. let position;
  402. let span = 0;
  403. if (defined(obb)) {
  404. boundingVolume = {
  405. box: [
  406. 0,
  407. 0,
  408. 0,
  409. obb.halfSize[0],
  410. 0,
  411. 0,
  412. 0,
  413. obb.halfSize[1],
  414. 0,
  415. 0,
  416. 0,
  417. obb.halfSize[2],
  418. ],
  419. };
  420. span = Math.max(
  421. Math.max(this._data.obb.halfSize[0], this._data.obb.halfSize[1]),
  422. this._data.obb.halfSize[2]
  423. );
  424. position = Ellipsoid.WGS84.cartographicToCartesian(geoPosition);
  425. } else {
  426. boundingVolume = {
  427. sphere: [0, 0, 0, mbs[3]],
  428. };
  429. position = Ellipsoid.WGS84.cartographicToCartesian(geoPosition);
  430. span = this._data.mbs[3];
  431. }
  432. span *= 2;
  433. // Compute the geometric error
  434. let metersPerPixel = Infinity;
  435. // Get the meters/pixel density required to pop the next LOD
  436. if (defined(this._data.lodThreshold)) {
  437. if (
  438. this._layer._data.nodePages.lodSelectionMetricType ===
  439. "maxScreenThresholdSQ"
  440. ) {
  441. const maxScreenThreshold = Math.sqrt(
  442. this._data.lodThreshold / (Math.PI * 0.25)
  443. );
  444. metersPerPixel = span / maxScreenThreshold;
  445. } else if (
  446. this._layer._data.nodePages.lodSelectionMetricType ===
  447. "maxScreenThreshold"
  448. ) {
  449. const maxScreenThreshold = this._data.lodThreshold;
  450. metersPerPixel = span / maxScreenThreshold;
  451. } else {
  452. // Other LOD selection types can only be used for point cloud data
  453. console.error("Invalid lodSelectionMetricType in Layer");
  454. }
  455. } else if (defined(this._data.lodSelection)) {
  456. for (
  457. let lodIndex = 0;
  458. lodIndex < this._data.lodSelection.length;
  459. lodIndex++
  460. ) {
  461. if (
  462. this._data.lodSelection[lodIndex].metricType === "maxScreenThreshold"
  463. ) {
  464. metersPerPixel = span / this._data.lodSelection[lodIndex].maxError;
  465. }
  466. }
  467. }
  468. if (metersPerPixel === Infinity) {
  469. metersPerPixel = 100000;
  470. }
  471. // Calculate the length of 16 pixels in order to trigger the screen space error
  472. const geometricError = metersPerPixel * 16;
  473. // Transformations
  474. const hpr = new HeadingPitchRoll(0, 0, 0);
  475. let orientation = Transforms.headingPitchRollQuaternion(position, hpr);
  476. if (defined(this._data.obb)) {
  477. orientation = new Quaternion(
  478. this._data.obb.quaternion[0],
  479. this._data.obb.quaternion[1],
  480. this._data.obb.quaternion[2],
  481. this._data.obb.quaternion[3]
  482. );
  483. }
  484. const rotationMatrix = Matrix3.fromQuaternion(orientation);
  485. const inverseRotationMatrix = Matrix3.inverse(rotationMatrix, new Matrix3());
  486. const globalTransform = new Matrix4(
  487. rotationMatrix[0],
  488. rotationMatrix[1],
  489. rotationMatrix[2],
  490. 0,
  491. rotationMatrix[3],
  492. rotationMatrix[4],
  493. rotationMatrix[5],
  494. 0,
  495. rotationMatrix[6],
  496. rotationMatrix[7],
  497. rotationMatrix[8],
  498. 0,
  499. position.x,
  500. position.y,
  501. position.z,
  502. 1
  503. );
  504. const inverseGlobalTransform = Matrix4.inverse(
  505. globalTransform,
  506. new Matrix4()
  507. );
  508. const localTransform = Matrix4.clone(globalTransform);
  509. if (defined(this._parent._globalTransform)) {
  510. Matrix4.multiply(
  511. globalTransform,
  512. this._parent._inverseGlobalTransform,
  513. localTransform
  514. );
  515. }
  516. this._globalTransform = globalTransform;
  517. this._inverseGlobalTransform = inverseGlobalTransform;
  518. this._inverseRotationMatrix = inverseRotationMatrix;
  519. // get children definition
  520. const childrenDefinition = [];
  521. for (let childIndex = 0; childIndex < this._children.length; childIndex++) {
  522. childrenDefinition.push(
  523. this._children[childIndex]._create3DTileDefinition()
  524. );
  525. }
  526. // Create a tile set
  527. const inPlaceTileDefinition = {
  528. children: childrenDefinition,
  529. refine: "REPLACE",
  530. boundingVolume: boundingVolume,
  531. transform: [
  532. localTransform[0],
  533. localTransform[4],
  534. localTransform[8],
  535. localTransform[12],
  536. localTransform[1],
  537. localTransform[5],
  538. localTransform[9],
  539. localTransform[13],
  540. localTransform[2],
  541. localTransform[6],
  542. localTransform[10],
  543. localTransform[14],
  544. localTransform[3],
  545. localTransform[7],
  546. localTransform[11],
  547. localTransform[15],
  548. ],
  549. content: {
  550. uri: defined(this._resource) ? this._resource.url : undefined,
  551. },
  552. geometricError: geometricError,
  553. };
  554. return inPlaceTileDefinition;
  555. };
  556. /**
  557. * @private
  558. */
  559. I3SNode.prototype._createI3SDecoderTask = async function (
  560. decodeI3STaskProcessor,
  561. data
  562. ) {
  563. // Prepare the data to send to the worker
  564. const parentData = data.geometryData._parent._data;
  565. const parentRotationInverseMatrix =
  566. data.geometryData._parent._inverseRotationMatrix;
  567. let longitude = 0.0;
  568. let latitude = 0.0;
  569. let height = 0.0;
  570. if (defined(parentData.obb)) {
  571. longitude = parentData.obb.center[0];
  572. latitude = parentData.obb.center[1];
  573. height = parentData.obb.center[2];
  574. } else if (defined(parentData.mbs)) {
  575. longitude = parentData.mbs[0];
  576. latitude = parentData.mbs[1];
  577. height = parentData.mbs[2];
  578. }
  579. const axisFlipRotation = Matrix3.fromRotationX(-CesiumMath.PI_OVER_TWO);
  580. const parentRotation = new Matrix3();
  581. Matrix3.multiply(
  582. axisFlipRotation,
  583. parentRotationInverseMatrix,
  584. parentRotation
  585. );
  586. const cartographicCenter = Cartographic.fromDegrees(
  587. longitude,
  588. latitude,
  589. height
  590. );
  591. const cartesianCenter = Ellipsoid.WGS84.cartographicToCartesian(
  592. cartographicCenter
  593. );
  594. const payload = {
  595. binaryData: data.geometryData._data,
  596. featureData:
  597. defined(data.featureData) && defined(data.featureData[0])
  598. ? data.featureData[0].data
  599. : undefined,
  600. schema: data.defaultGeometrySchema,
  601. bufferInfo: data.geometryData._geometryBufferInfo,
  602. ellipsoidRadiiSquare: Ellipsoid.WGS84.radiiSquared,
  603. url: data.url,
  604. geoidDataList: data.geometryData._dataProvider._geoidDataList,
  605. cartographicCenter: cartographicCenter,
  606. cartesianCenter: cartesianCenter,
  607. parentRotation: parentRotation,
  608. };
  609. const transferrableObjects = [];
  610. return decodeI3STaskProcessor.scheduleTask(payload, transferrableObjects);
  611. };
  612. /**
  613. * @private
  614. */
  615. I3SNode.prototype._createContentURL = async function () {
  616. let rawGltf = {
  617. scene: 0,
  618. scenes: [
  619. {
  620. nodes: [0],
  621. },
  622. ],
  623. nodes: [
  624. {
  625. name: "singleNode",
  626. },
  627. ],
  628. meshes: [],
  629. buffers: [],
  630. bufferViews: [],
  631. accessors: [],
  632. materials: [],
  633. textures: [],
  634. images: [],
  635. samplers: [],
  636. asset: {
  637. version: "2.0",
  638. },
  639. };
  640. const decodeI3STaskProcessor = await this._dataProvider.getDecoderTaskProcessor();
  641. // Load the geometry data
  642. const dataPromises = [this._loadGeometryData()];
  643. if (this._dataProvider.legacyVersion16) {
  644. dataPromises.push(this._loadFeatureData());
  645. }
  646. const that = this;
  647. return Promise.all(dataPromises).then(function () {
  648. // Binary glTF
  649. let generateGltfPromise = Promise.resolve();
  650. if (defined(that._geometryData) && that._geometryData.length > 0) {
  651. const parameters = {
  652. geometryData: that._geometryData[0],
  653. featureData: that._featureData,
  654. defaultGeometrySchema: that._layer._data.store.defaultGeometrySchema,
  655. url: that._geometryData[0].resource.url,
  656. tile: that._tile,
  657. };
  658. const promise = that._createI3SDecoderTask(
  659. decodeI3STaskProcessor,
  660. parameters
  661. );
  662. if (!defined(promise)) {
  663. // Postponed
  664. return;
  665. }
  666. generateGltfPromise = promise.then(function (result) {
  667. rawGltf = parameters.geometryData._generateGltf(
  668. result.meshData.nodesInScene,
  669. result.meshData.nodes,
  670. result.meshData.meshes,
  671. result.meshData.buffers,
  672. result.meshData.bufferViews,
  673. result.meshData.accessors
  674. );
  675. that._geometryData[0]._customAttributes =
  676. result.meshData._customAttributes;
  677. });
  678. }
  679. return generateGltfPromise.then(function () {
  680. const binaryGltfData = that._dataProvider._binarizeGltf(rawGltf);
  681. const glbDataBlob = new Blob([binaryGltfData], {
  682. type: "application/binary",
  683. });
  684. return URL.createObjectURL(glbDataBlob);
  685. });
  686. });
  687. };
  688. // Reimplement Cesium3DTile.prototype.requestContent so that
  689. // We get a chance to load our own gltf from I3S data
  690. Cesium3DTile.prototype._hookedRequestContent =
  691. Cesium3DTile.prototype.requestContent;
  692. /**
  693. * Requests the tile's content.
  694. * <p>
  695. * The request may not be made if the Cesium Request Scheduler can't prioritize it.
  696. * </p>
  697. *
  698. * @return {Promise<Cesium3DTileContent>|undefined} A promise that resolves when the request completes, or undefined if there is no request needed, or the request cannot be scheduled.
  699. * @private
  700. */
  701. Cesium3DTile.prototype.requestContent = function () {
  702. if (!this.tileset._isI3STileSet) {
  703. return this._hookedRequestContent();
  704. }
  705. if (!this._isLoading) {
  706. this._isLoading = true;
  707. return this._i3sNode
  708. ._createContentURL()
  709. .then((url) => {
  710. if (!defined(url)) {
  711. this._isLoading = false;
  712. return;
  713. }
  714. this._contentResource = new Resource({ url: url });
  715. return this._hookedRequestContent();
  716. })
  717. .then((content) => {
  718. this._isLoading = false;
  719. return content;
  720. });
  721. }
  722. };
  723. function bilinearInterpolate(tx, ty, h00, h10, h01, h11) {
  724. const a = h00 * (1 - tx) + h10 * tx;
  725. const b = h01 * (1 - tx) + h11 * tx;
  726. return a * (1 - ty) + b * ty;
  727. }
  728. function sampleMap(u, v, width, data) {
  729. const address = u + v * width;
  730. return data[address];
  731. }
  732. function sampleGeoid(sampleX, sampleY, geoidData) {
  733. const extent = geoidData.nativeExtent;
  734. let x =
  735. ((sampleX - extent.west) / (extent.east - extent.west)) *
  736. (geoidData.width - 1);
  737. let y =
  738. ((sampleY - extent.south) / (extent.north - extent.south)) *
  739. (geoidData.height - 1);
  740. const xi = Math.floor(x);
  741. let yi = Math.floor(y);
  742. x -= xi;
  743. y -= yi;
  744. const xNext = xi < geoidData.width ? xi + 1 : xi;
  745. let yNext = yi < geoidData.height ? yi + 1 : yi;
  746. yi = geoidData.height - 1 - yi;
  747. yNext = geoidData.height - 1 - yNext;
  748. const h00 = sampleMap(xi, yi, geoidData.width, geoidData.buffer);
  749. const h10 = sampleMap(xNext, yi, geoidData.width, geoidData.buffer);
  750. const h01 = sampleMap(xi, yNext, geoidData.width, geoidData.buffer);
  751. const h11 = sampleMap(xNext, yNext, geoidData.width, geoidData.buffer);
  752. let finalHeight = bilinearInterpolate(x, y, h00, h10, h01, h11);
  753. finalHeight = finalHeight * geoidData.scale + geoidData.offset;
  754. return finalHeight;
  755. }
  756. Object.defineProperties(Cesium3DTile.prototype, {
  757. /**
  758. * Gets the I3S Node for the tile.
  759. * @memberof Cesium3DTile.prototype
  760. * @type {string}
  761. */
  762. i3sNode: {
  763. get: function () {
  764. return this._i3sNode;
  765. },
  766. },
  767. });
  768. export default I3SNode;