Geometry3DTileContent.js 16 KB

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