Geometry3DTileContent.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defined from "../Core/defined.js";
  4. import deprecationWarning from "../Core/deprecationWarning.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import DeveloperError from "../Core/DeveloperError.js";
  7. import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import RuntimeError from "../Core/RuntimeError.js";
  10. import Cesium3DTileBatchTable from "./Cesium3DTileBatchTable.js";
  11. import Vector3DTileGeometry from "./Vector3DTileGeometry.js";
  12. /**
  13. * <p>
  14. * Implements the {@link Cesium3DTileContent} interface.
  15. * </p>
  16. *
  17. * @alias Geometry3DTileContent
  18. * @constructor
  19. *
  20. * @private
  21. */
  22. function Geometry3DTileContent(
  23. tileset,
  24. tile,
  25. resource,
  26. arrayBuffer,
  27. byteOffset
  28. ) {
  29. this._tileset = tileset;
  30. this._tile = tile;
  31. this._resource = resource;
  32. this._geometries = undefined;
  33. this._metadata = undefined;
  34. this._batchTable = undefined;
  35. this._features = undefined;
  36. /**
  37. * Part of the {@link Cesium3DTileContent} interface.
  38. */
  39. this.featurePropertiesDirty = false;
  40. this._group = undefined;
  41. this._ready = false;
  42. // This is for backwards compatibility. It can be removed once readyPromise is removed.
  43. this._resolveContent = undefined;
  44. this._readyPromise = new Promise((resolve) => {
  45. this._resolveContent = resolve;
  46. });
  47. initialize(this, arrayBuffer, byteOffset);
  48. }
  49. Object.defineProperties(Geometry3DTileContent.prototype, {
  50. featuresLength: {
  51. get: function () {
  52. return defined(this._batchTable) ? this._batchTable.featuresLength : 0;
  53. },
  54. },
  55. pointsLength: {
  56. get: function () {
  57. return 0;
  58. },
  59. },
  60. trianglesLength: {
  61. get: function () {
  62. if (defined(this._geometries)) {
  63. return this._geometries.trianglesLength;
  64. }
  65. return 0;
  66. },
  67. },
  68. geometryByteLength: {
  69. get: function () {
  70. if (defined(this._geometries)) {
  71. return this._geometries.geometryByteLength;
  72. }
  73. return 0;
  74. },
  75. },
  76. texturesByteLength: {
  77. get: function () {
  78. return 0;
  79. },
  80. },
  81. batchTableByteLength: {
  82. get: function () {
  83. return defined(this._batchTable)
  84. ? this._batchTable.batchTableByteLength
  85. : 0;
  86. },
  87. },
  88. innerContents: {
  89. get: function () {
  90. return undefined;
  91. },
  92. },
  93. /**
  94. * Returns true when the tile's content is ready to render; otherwise false
  95. *
  96. * @memberof Geometry3DTileContent.prototype
  97. *
  98. * @type {boolean}
  99. * @readonly
  100. * @private
  101. */
  102. ready: {
  103. get: function () {
  104. return this._ready;
  105. },
  106. },
  107. /**
  108. * Gets the promise that will be resolved when the tile's content is ready to render.
  109. *
  110. * @memberof Geometry3DTileContent.prototype
  111. *
  112. * @type {Promise<Geometry3DTileContent>}
  113. * @readonly
  114. * @deprecated
  115. * @private
  116. */
  117. readyPromise: {
  118. get: function () {
  119. deprecationWarning(
  120. "Geometry3DTileContent.readyPromise",
  121. "Geometry3DTileContent.readyPromise was deprecated in CesiumJS 1.104. It will be removed in 1.107. Wait for Geometry3DTileContent.ready to return true instead."
  122. );
  123. return this._readyPromise;
  124. },
  125. },
  126. tileset: {
  127. get: function () {
  128. return this._tileset;
  129. },
  130. },
  131. tile: {
  132. get: function () {
  133. return this._tile;
  134. },
  135. },
  136. url: {
  137. get: function () {
  138. return this._resource.getUrlComponent(true);
  139. },
  140. },
  141. metadata: {
  142. get: function () {
  143. return this._metadata;
  144. },
  145. set: function (value) {
  146. this._metadata = value;
  147. },
  148. },
  149. batchTable: {
  150. get: function () {
  151. return this._batchTable;
  152. },
  153. },
  154. group: {
  155. get: function () {
  156. return this._group;
  157. },
  158. set: function (value) {
  159. this._group = value;
  160. },
  161. },
  162. });
  163. function createColorChangedCallback(content) {
  164. return function (batchId, color) {
  165. if (defined(content._geometries)) {
  166. content._geometries.updateCommands(batchId, color);
  167. }
  168. };
  169. }
  170. function getBatchIds(featureTableJson, featureTableBinary) {
  171. let boxBatchIds;
  172. let cylinderBatchIds;
  173. let ellipsoidBatchIds;
  174. let sphereBatchIds;
  175. let i;
  176. const numberOfBoxes = defaultValue(featureTableJson.BOXES_LENGTH, 0);
  177. const numberOfCylinders = defaultValue(featureTableJson.CYLINDERS_LENGTH, 0);
  178. const numberOfEllipsoids = defaultValue(
  179. featureTableJson.ELLIPSOIDS_LENGTH,
  180. 0
  181. );
  182. const numberOfSpheres = defaultValue(featureTableJson.SPHERES_LENGTH, 0);
  183. if (numberOfBoxes > 0 && defined(featureTableJson.BOX_BATCH_IDS)) {
  184. const boxBatchIdsByteOffset =
  185. featureTableBinary.byteOffset + featureTableJson.BOX_BATCH_IDS.byteOffset;
  186. boxBatchIds = new Uint16Array(
  187. featureTableBinary.buffer,
  188. boxBatchIdsByteOffset,
  189. numberOfBoxes
  190. );
  191. }
  192. if (numberOfCylinders > 0 && defined(featureTableJson.CYLINDER_BATCH_IDS)) {
  193. const cylinderBatchIdsByteOffset =
  194. featureTableBinary.byteOffset +
  195. featureTableJson.CYLINDER_BATCH_IDS.byteOffset;
  196. cylinderBatchIds = new Uint16Array(
  197. featureTableBinary.buffer,
  198. cylinderBatchIdsByteOffset,
  199. numberOfCylinders
  200. );
  201. }
  202. if (numberOfEllipsoids > 0 && defined(featureTableJson.ELLIPSOID_BATCH_IDS)) {
  203. const ellipsoidBatchIdsByteOffset =
  204. featureTableBinary.byteOffset +
  205. featureTableJson.ELLIPSOID_BATCH_IDS.byteOffset;
  206. ellipsoidBatchIds = new Uint16Array(
  207. featureTableBinary.buffer,
  208. ellipsoidBatchIdsByteOffset,
  209. numberOfEllipsoids
  210. );
  211. }
  212. if (numberOfSpheres > 0 && defined(featureTableJson.SPHERE_BATCH_IDS)) {
  213. const sphereBatchIdsByteOffset =
  214. featureTableBinary.byteOffset +
  215. featureTableJson.SPHERE_BATCH_IDS.byteOffset;
  216. sphereBatchIds = new Uint16Array(
  217. featureTableBinary.buffer,
  218. sphereBatchIdsByteOffset,
  219. numberOfSpheres
  220. );
  221. }
  222. const atLeastOneDefined =
  223. defined(boxBatchIds) ||
  224. defined(cylinderBatchIds) ||
  225. defined(ellipsoidBatchIds) ||
  226. defined(sphereBatchIds);
  227. const atLeastOneUndefined =
  228. (numberOfBoxes > 0 && !defined(boxBatchIds)) ||
  229. (numberOfCylinders > 0 && !defined(cylinderBatchIds)) ||
  230. (numberOfEllipsoids > 0 && !defined(ellipsoidBatchIds)) ||
  231. (numberOfSpheres > 0 && !defined(sphereBatchIds));
  232. if (atLeastOneDefined && atLeastOneUndefined) {
  233. throw new RuntimeError(
  234. "If one group of batch ids is defined, then all batch ids must be defined"
  235. );
  236. }
  237. const allUndefinedBatchIds =
  238. !defined(boxBatchIds) &&
  239. !defined(cylinderBatchIds) &&
  240. !defined(ellipsoidBatchIds) &&
  241. !defined(sphereBatchIds);
  242. if (allUndefinedBatchIds) {
  243. let id = 0;
  244. if (!defined(boxBatchIds) && numberOfBoxes > 0) {
  245. boxBatchIds = new Uint16Array(numberOfBoxes);
  246. for (i = 0; i < numberOfBoxes; ++i) {
  247. boxBatchIds[i] = id++;
  248. }
  249. }
  250. if (!defined(cylinderBatchIds) && numberOfCylinders > 0) {
  251. cylinderBatchIds = new Uint16Array(numberOfCylinders);
  252. for (i = 0; i < numberOfCylinders; ++i) {
  253. cylinderBatchIds[i] = id++;
  254. }
  255. }
  256. if (!defined(ellipsoidBatchIds) && numberOfEllipsoids > 0) {
  257. ellipsoidBatchIds = new Uint16Array(numberOfEllipsoids);
  258. for (i = 0; i < numberOfEllipsoids; ++i) {
  259. ellipsoidBatchIds[i] = id++;
  260. }
  261. }
  262. if (!defined(sphereBatchIds) && numberOfSpheres > 0) {
  263. sphereBatchIds = new Uint16Array(numberOfSpheres);
  264. for (i = 0; i < numberOfSpheres; ++i) {
  265. sphereBatchIds[i] = id++;
  266. }
  267. }
  268. }
  269. return {
  270. boxes: boxBatchIds,
  271. cylinders: cylinderBatchIds,
  272. ellipsoids: ellipsoidBatchIds,
  273. spheres: sphereBatchIds,
  274. };
  275. }
  276. const sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  277. function initialize(content, arrayBuffer, byteOffset) {
  278. byteOffset = defaultValue(byteOffset, 0);
  279. const uint8Array = new Uint8Array(arrayBuffer);
  280. const view = new DataView(arrayBuffer);
  281. byteOffset += sizeOfUint32; // Skip magic number
  282. const version = view.getUint32(byteOffset, true);
  283. if (version !== 1) {
  284. throw new RuntimeError(
  285. `Only Geometry tile version 1 is supported. Version ${version} is not.`
  286. );
  287. }
  288. byteOffset += sizeOfUint32;
  289. const byteLength = view.getUint32(byteOffset, true);
  290. byteOffset += sizeOfUint32;
  291. if (byteLength === 0) {
  292. content._ready = true;
  293. content._resolveContent(content);
  294. return;
  295. }
  296. const featureTableJSONByteLength = view.getUint32(byteOffset, true);
  297. byteOffset += sizeOfUint32;
  298. if (featureTableJSONByteLength === 0) {
  299. throw new RuntimeError(
  300. "Feature table must have a byte length greater than zero"
  301. );
  302. }
  303. const featureTableBinaryByteLength = view.getUint32(byteOffset, true);
  304. byteOffset += sizeOfUint32;
  305. const batchTableJSONByteLength = view.getUint32(byteOffset, true);
  306. byteOffset += sizeOfUint32;
  307. const batchTableBinaryByteLength = view.getUint32(byteOffset, true);
  308. byteOffset += sizeOfUint32;
  309. const featureTableJson = getJsonFromTypedArray(
  310. uint8Array,
  311. byteOffset,
  312. featureTableJSONByteLength
  313. );
  314. byteOffset += featureTableJSONByteLength;
  315. const featureTableBinary = new Uint8Array(
  316. arrayBuffer,
  317. byteOffset,
  318. featureTableBinaryByteLength
  319. );
  320. byteOffset += featureTableBinaryByteLength;
  321. let batchTableJson;
  322. let batchTableBinary;
  323. if (batchTableJSONByteLength > 0) {
  324. // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the
  325. // arraybuffer/string compressed in memory and then decompress it when it is first accessed.
  326. //
  327. // We could also make another request for it, but that would make the property set/get
  328. // API async, and would double the number of numbers in some cases.
  329. batchTableJson = getJsonFromTypedArray(
  330. uint8Array,
  331. byteOffset,
  332. batchTableJSONByteLength
  333. );
  334. byteOffset += batchTableJSONByteLength;
  335. if (batchTableBinaryByteLength > 0) {
  336. // Has a batch table binary
  337. batchTableBinary = new Uint8Array(
  338. arrayBuffer,
  339. byteOffset,
  340. batchTableBinaryByteLength
  341. );
  342. // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
  343. batchTableBinary = new Uint8Array(batchTableBinary);
  344. }
  345. }
  346. const numberOfBoxes = defaultValue(featureTableJson.BOXES_LENGTH, 0);
  347. const numberOfCylinders = defaultValue(featureTableJson.CYLINDERS_LENGTH, 0);
  348. const numberOfEllipsoids = defaultValue(
  349. featureTableJson.ELLIPSOIDS_LENGTH,
  350. 0
  351. );
  352. const numberOfSpheres = defaultValue(featureTableJson.SPHERES_LENGTH, 0);
  353. const totalPrimitives =
  354. numberOfBoxes + numberOfCylinders + numberOfEllipsoids + numberOfSpheres;
  355. const batchTable = new Cesium3DTileBatchTable(
  356. content,
  357. totalPrimitives,
  358. batchTableJson,
  359. batchTableBinary,
  360. createColorChangedCallback(content)
  361. );
  362. content._batchTable = batchTable;
  363. if (totalPrimitives === 0) {
  364. return;
  365. }
  366. const modelMatrix = content.tile.computedTransform;
  367. let center;
  368. if (defined(featureTableJson.RTC_CENTER)) {
  369. center = Cartesian3.unpack(featureTableJson.RTC_CENTER);
  370. Matrix4.multiplyByPoint(modelMatrix, center, center);
  371. }
  372. const batchIds = getBatchIds(featureTableJson, featureTableBinary);
  373. if (
  374. numberOfBoxes > 0 ||
  375. numberOfCylinders > 0 ||
  376. numberOfEllipsoids > 0 ||
  377. numberOfSpheres > 0
  378. ) {
  379. let boxes;
  380. let cylinders;
  381. let ellipsoids;
  382. let spheres;
  383. if (numberOfBoxes > 0) {
  384. const boxesByteOffset =
  385. featureTableBinary.byteOffset + featureTableJson.BOXES.byteOffset;
  386. boxes = new Float32Array(
  387. featureTableBinary.buffer,
  388. boxesByteOffset,
  389. Vector3DTileGeometry.packedBoxLength * numberOfBoxes
  390. );
  391. }
  392. if (numberOfCylinders > 0) {
  393. const cylindersByteOffset =
  394. featureTableBinary.byteOffset + featureTableJson.CYLINDERS.byteOffset;
  395. cylinders = new Float32Array(
  396. featureTableBinary.buffer,
  397. cylindersByteOffset,
  398. Vector3DTileGeometry.packedCylinderLength * numberOfCylinders
  399. );
  400. }
  401. if (numberOfEllipsoids > 0) {
  402. const ellipsoidsByteOffset =
  403. featureTableBinary.byteOffset + featureTableJson.ELLIPSOIDS.byteOffset;
  404. ellipsoids = new Float32Array(
  405. featureTableBinary.buffer,
  406. ellipsoidsByteOffset,
  407. Vector3DTileGeometry.packedEllipsoidLength * numberOfEllipsoids
  408. );
  409. }
  410. if (numberOfSpheres > 0) {
  411. const spheresByteOffset =
  412. featureTableBinary.byteOffset + featureTableJson.SPHERES.byteOffset;
  413. spheres = new Float32Array(
  414. featureTableBinary.buffer,
  415. spheresByteOffset,
  416. Vector3DTileGeometry.packedSphereLength * numberOfSpheres
  417. );
  418. }
  419. content._geometries = new Vector3DTileGeometry({
  420. boxes: boxes,
  421. boxBatchIds: batchIds.boxes,
  422. cylinders: cylinders,
  423. cylinderBatchIds: batchIds.cylinders,
  424. ellipsoids: ellipsoids,
  425. ellipsoidBatchIds: batchIds.ellipsoids,
  426. spheres: spheres,
  427. sphereBatchIds: batchIds.spheres,
  428. center: center,
  429. modelMatrix: modelMatrix,
  430. batchTable: batchTable,
  431. boundingVolume: content.tile.boundingVolume.boundingVolume,
  432. });
  433. return content;
  434. }
  435. return Promise.resolve(content);
  436. }
  437. function createFeatures(content) {
  438. const featuresLength = content.featuresLength;
  439. if (!defined(content._features) && featuresLength > 0) {
  440. const features = new Array(featuresLength);
  441. if (defined(content._geometries)) {
  442. content._geometries.createFeatures(content, features);
  443. }
  444. content._features = features;
  445. }
  446. }
  447. Geometry3DTileContent.prototype.hasProperty = function (batchId, name) {
  448. return this._batchTable.hasProperty(batchId, name);
  449. };
  450. Geometry3DTileContent.prototype.getFeature = function (batchId) {
  451. //>>includeStart('debug', pragmas.debug);
  452. const featuresLength = this.featuresLength;
  453. if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) {
  454. throw new DeveloperError(
  455. `batchId is required and between zero and featuresLength - 1 (${
  456. featuresLength - 1
  457. }).`
  458. );
  459. }
  460. //>>includeEnd('debug');
  461. createFeatures(this);
  462. return this._features[batchId];
  463. };
  464. Geometry3DTileContent.prototype.applyDebugSettings = function (enabled, color) {
  465. if (defined(this._geometries)) {
  466. this._geometries.applyDebugSettings(enabled, color);
  467. }
  468. };
  469. Geometry3DTileContent.prototype.applyStyle = function (style) {
  470. createFeatures(this);
  471. if (defined(this._geometries)) {
  472. this._geometries.applyStyle(style, this._features);
  473. }
  474. };
  475. Geometry3DTileContent.prototype.update = function (tileset, frameState) {
  476. if (defined(this._geometries)) {
  477. this._geometries.classificationType = this._tileset.classificationType;
  478. this._geometries.debugWireframe = this._tileset.debugWireframe;
  479. this._geometries.update(frameState);
  480. }
  481. if (defined(this._batchTable) && this._geometries.ready) {
  482. this._batchTable.update(tileset, frameState);
  483. this._ready = true;
  484. this._resolveContent(this);
  485. }
  486. };
  487. Geometry3DTileContent.prototype.isDestroyed = function () {
  488. return false;
  489. };
  490. Geometry3DTileContent.prototype.destroy = function () {
  491. this._geometries = this._geometries && this._geometries.destroy();
  492. this._batchTable = this._batchTable && this._batchTable.destroy();
  493. return destroyObject(this);
  494. };
  495. export default Geometry3DTileContent;