Geometry3DTileContent.js 14 KB

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