Vector3DTileContent.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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 Ellipsoid from '../Core/Ellipsoid.js';
  8. import getStringFromTypedArray from '../Core/getStringFromTypedArray.js';
  9. import CesiumMath from '../Core/Math.js';
  10. import Matrix4 from '../Core/Matrix4.js';
  11. import Rectangle from '../Core/Rectangle.js';
  12. import RuntimeError from '../Core/RuntimeError.js';
  13. import when from '../ThirdParty/when.js';
  14. import Cesium3DTileBatchTable from './Cesium3DTileBatchTable.js';
  15. import Vector3DTilePoints from './Vector3DTilePoints.js';
  16. import Vector3DTilePolygons from './Vector3DTilePolygons.js';
  17. import Vector3DTilePolylines from './Vector3DTilePolylines.js';
  18. /**
  19. * Represents the contents of a
  20. * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/3d-tiles-next/TileFormats/VectorData|Vector}
  21. * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification|3D Tiles} tileset.
  22. * <p>
  23. * Implements the {@link Cesium3DTileContent} interface.
  24. * </p>
  25. *
  26. * @alias Vector3DTileContent
  27. * @constructor
  28. *
  29. * @private
  30. */
  31. function Vector3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) {
  32. this._tileset = tileset;
  33. this._tile = tile;
  34. this._resource = resource;
  35. this._polygons = undefined;
  36. this._polylines = undefined;
  37. this._points = undefined;
  38. this._contentReadyPromise = undefined;
  39. this._readyPromise = when.defer();
  40. this._batchTable = undefined;
  41. this._features = undefined;
  42. /**
  43. * Part of the {@link Cesium3DTileContent} interface.
  44. */
  45. this.featurePropertiesDirty = false;
  46. initialize(this, arrayBuffer, byteOffset);
  47. }
  48. defineProperties(Vector3DTileContent.prototype, {
  49. featuresLength : {
  50. get : function() {
  51. return defined(this._batchTable) ? this._batchTable.featuresLength : 0;
  52. }
  53. },
  54. pointsLength : {
  55. get : function() {
  56. if (defined(this._points)) {
  57. return this._points.pointsLength;
  58. }
  59. return 0;
  60. }
  61. },
  62. trianglesLength : {
  63. get : function() {
  64. var trianglesLength = 0;
  65. if (defined(this._polygons)) {
  66. trianglesLength += this._polygons.trianglesLength;
  67. }
  68. if (defined(this._polylines)) {
  69. trianglesLength += this._polylines.trianglesLength;
  70. }
  71. return trianglesLength;
  72. }
  73. },
  74. geometryByteLength : {
  75. get : function() {
  76. var geometryByteLength = 0;
  77. if (defined(this._polygons)) {
  78. geometryByteLength += this._polygons.geometryByteLength;
  79. }
  80. if (defined(this._polylines)) {
  81. geometryByteLength += this._polylines.geometryByteLength;
  82. }
  83. return geometryByteLength;
  84. }
  85. },
  86. texturesByteLength : {
  87. get : function() {
  88. if (defined(this._points)) {
  89. return this._points.texturesByteLength;
  90. }
  91. return 0;
  92. }
  93. },
  94. batchTableByteLength : {
  95. get : function() {
  96. return defined(this._batchTable) ? this._batchTable.memorySizeInBytes : 0;
  97. }
  98. },
  99. innerContents : {
  100. get : function() {
  101. return undefined;
  102. }
  103. },
  104. readyPromise : {
  105. get : function() {
  106. return this._readyPromise.promise;
  107. }
  108. },
  109. tileset : {
  110. get : function() {
  111. return this._tileset;
  112. }
  113. },
  114. tile : {
  115. get : function() {
  116. return this._tile;
  117. }
  118. },
  119. url : {
  120. get : function() {
  121. return this._resource.getUrlComponent(true);
  122. }
  123. },
  124. batchTable : {
  125. get : function() {
  126. return this._batchTable;
  127. }
  128. }
  129. });
  130. function createColorChangedCallback(content) {
  131. return function(batchId, color) {
  132. if (defined(content._polygons)) {
  133. content._polygons.updateCommands(batchId, color);
  134. }
  135. };
  136. }
  137. function getBatchIds(featureTableJson, featureTableBinary) {
  138. var polygonBatchIds;
  139. var polylineBatchIds;
  140. var pointBatchIds;
  141. var i;
  142. var numberOfPolygons = defaultValue(featureTableJson.POLYGONS_LENGTH, 0);
  143. var numberOfPolylines = defaultValue(featureTableJson.POLYLINES_LENGTH, 0);
  144. var numberOfPoints = defaultValue(featureTableJson.POINTS_LENGTH, 0);
  145. if (numberOfPolygons > 0 && defined(featureTableJson.POLYGON_BATCH_IDS)) {
  146. var polygonBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYGON_BATCH_IDS.byteOffset;
  147. polygonBatchIds = new Uint16Array(featureTableBinary.buffer, polygonBatchIdsByteOffset, numberOfPolygons);
  148. }
  149. if (numberOfPolylines > 0 && defined(featureTableJson.POLYLINE_BATCH_IDS)) {
  150. var polylineBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYLINE_BATCH_IDS.byteOffset;
  151. polylineBatchIds = new Uint16Array(featureTableBinary.buffer, polylineBatchIdsByteOffset, numberOfPolylines);
  152. }
  153. if (numberOfPoints > 0 && defined(featureTableJson.POINT_BATCH_IDS)) {
  154. var pointBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.POINT_BATCH_IDS.byteOffset;
  155. pointBatchIds = new Uint16Array(featureTableBinary.buffer, pointBatchIdsByteOffset, numberOfPoints);
  156. }
  157. var atLeastOneDefined = defined(polygonBatchIds) || defined(polylineBatchIds) || defined(pointBatchIds);
  158. var atLeastOneUndefined = (numberOfPolygons > 0 && !defined(polygonBatchIds)) ||
  159. (numberOfPolylines > 0 && !defined(polylineBatchIds)) ||
  160. (numberOfPoints > 0 && !defined(pointBatchIds));
  161. if (atLeastOneDefined && atLeastOneUndefined) {
  162. throw new RuntimeError('If one group of batch ids is defined, then all batch ids must be defined.');
  163. }
  164. var allUndefinedBatchIds = !defined(polygonBatchIds) && !defined(polylineBatchIds) && !defined(pointBatchIds);
  165. if (allUndefinedBatchIds) {
  166. var id = 0;
  167. if (!defined(polygonBatchIds) && numberOfPolygons > 0) {
  168. polygonBatchIds = new Uint16Array(numberOfPolygons);
  169. for (i = 0; i < numberOfPolygons; ++i) {
  170. polygonBatchIds[i] = id++;
  171. }
  172. }
  173. if (!defined(polylineBatchIds) && numberOfPolylines > 0) {
  174. polylineBatchIds = new Uint16Array(numberOfPolylines);
  175. for (i = 0; i < numberOfPolylines; ++i) {
  176. polylineBatchIds[i] = id++;
  177. }
  178. }
  179. if (!defined(pointBatchIds) && numberOfPoints > 0) {
  180. pointBatchIds = new Uint16Array(numberOfPoints);
  181. for (i = 0; i < numberOfPoints; ++i) {
  182. pointBatchIds[i] = id++;
  183. }
  184. }
  185. }
  186. return {
  187. polygons : polygonBatchIds,
  188. polylines : polylineBatchIds,
  189. points : pointBatchIds
  190. };
  191. }
  192. var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
  193. var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  194. function initialize(content, arrayBuffer, byteOffset) {
  195. byteOffset = defaultValue(byteOffset, 0);
  196. var uint8Array = new Uint8Array(arrayBuffer);
  197. var view = new DataView(arrayBuffer);
  198. byteOffset += sizeOfUint32; // Skip magic number
  199. var version = view.getUint32(byteOffset, true);
  200. if (version !== 1) {
  201. throw new RuntimeError('Only Vector tile version 1 is supported. Version ' + version + ' is not.');
  202. }
  203. byteOffset += sizeOfUint32;
  204. var byteLength = view.getUint32(byteOffset, true);
  205. byteOffset += sizeOfUint32;
  206. if (byteLength === 0) {
  207. content._readyPromise.resolve(content);
  208. return;
  209. }
  210. var featureTableJSONByteLength = view.getUint32(byteOffset, true);
  211. byteOffset += sizeOfUint32;
  212. if (featureTableJSONByteLength === 0) {
  213. throw new RuntimeError('Feature table must have a byte length greater than zero');
  214. }
  215. var featureTableBinaryByteLength = view.getUint32(byteOffset, true);
  216. byteOffset += sizeOfUint32;
  217. var batchTableJSONByteLength = view.getUint32(byteOffset, true);
  218. byteOffset += sizeOfUint32;
  219. var batchTableBinaryByteLength = view.getUint32(byteOffset, true);
  220. byteOffset += sizeOfUint32;
  221. var indicesByteLength = view.getUint32(byteOffset, true);
  222. byteOffset += sizeOfUint32;
  223. var positionByteLength = view.getUint32(byteOffset, true);
  224. byteOffset += sizeOfUint32;
  225. var polylinePositionByteLength = view.getUint32(byteOffset, true);
  226. byteOffset += sizeOfUint32;
  227. var pointsPositionByteLength = view.getUint32(byteOffset, true);
  228. byteOffset += sizeOfUint32;
  229. var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJSONByteLength);
  230. var featureTableJson = JSON.parse(featureTableString);
  231. byteOffset += featureTableJSONByteLength;
  232. var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength);
  233. byteOffset += featureTableBinaryByteLength;
  234. var batchTableJson;
  235. var batchTableBinary;
  236. if (batchTableJSONByteLength > 0) {
  237. // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the
  238. // arraybuffer/string compressed in memory and then decompress it when it is first accessed.
  239. //
  240. // We could also make another request for it, but that would make the property set/get
  241. // API async, and would double the number of numbers in some cases.
  242. var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJSONByteLength);
  243. batchTableJson = JSON.parse(batchTableString);
  244. byteOffset += batchTableJSONByteLength;
  245. if (batchTableBinaryByteLength > 0) {
  246. // Has a batch table binary
  247. batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength);
  248. // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
  249. batchTableBinary = new Uint8Array(batchTableBinary);
  250. byteOffset += batchTableBinaryByteLength;
  251. }
  252. }
  253. var numberOfPolygons = defaultValue(featureTableJson.POLYGONS_LENGTH, 0);
  254. var numberOfPolylines = defaultValue(featureTableJson.POLYLINES_LENGTH, 0);
  255. var numberOfPoints = defaultValue(featureTableJson.POINTS_LENGTH, 0);
  256. var totalPrimitives = numberOfPolygons + numberOfPolylines + numberOfPoints;
  257. var batchTable = new Cesium3DTileBatchTable(content, totalPrimitives, batchTableJson, batchTableBinary, createColorChangedCallback(content));
  258. content._batchTable = batchTable;
  259. if (totalPrimitives === 0) {
  260. return;
  261. }
  262. var rectangle;
  263. var minHeight;
  264. var maxHeight;
  265. if (defined(featureTableJson.REGION)) {
  266. var region = featureTableJson.REGION;
  267. rectangle = Rectangle.unpack(region);
  268. minHeight = region[4];
  269. maxHeight = region[5];
  270. } else {
  271. throw new RuntimeError('REGION is required in the feature table.');
  272. }
  273. var modelMatrix = content._tile.computedTransform;
  274. var center;
  275. if (defined(featureTableJson.RTC_CENTER)) {
  276. center = Cartesian3.unpack(featureTableJson.RTC_CENTER);
  277. Matrix4.multiplyByPoint(modelMatrix, center, center);
  278. } else {
  279. center = Rectangle.center(rectangle);
  280. center.height = CesiumMath.lerp(minHeight, maxHeight, 0.5);
  281. center = Ellipsoid.WGS84.cartographicToCartesian(center);
  282. }
  283. var batchIds = getBatchIds(featureTableJson, featureTableBinary);
  284. byteOffset += byteOffset % 4;
  285. if (numberOfPolygons > 0) {
  286. var indices = new Uint32Array(arrayBuffer, byteOffset, indicesByteLength / sizeOfUint32);
  287. byteOffset += indicesByteLength;
  288. var polygonPositions = new Uint16Array(arrayBuffer, byteOffset, positionByteLength / sizeOfUint16);
  289. byteOffset += positionByteLength;
  290. var polygonCountByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYGON_COUNT.byteOffset;
  291. var counts = new Uint32Array(featureTableBinary.buffer, polygonCountByteOffset, numberOfPolygons);
  292. var polygonIndexCountByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYGON_INDEX_COUNT.byteOffset;
  293. var indexCounts = new Uint32Array(featureTableBinary.buffer, polygonIndexCountByteOffset, numberOfPolygons);
  294. var polygonMinimumHeights;
  295. var polygonMaximumHeights;
  296. if (defined(featureTableJson.POLYGON_MINIMUM_HEIGHTS) && defined(featureTableJson.POLYGON_MAXIMUM_HEIGHTS)) {
  297. var polygonMinimumHeightsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYGON_MINIMUM_HEIGHTS.byteOffset;
  298. polygonMinimumHeights = new Float32Array(featureTableBinary.buffer, polygonMinimumHeightsByteOffset, numberOfPolygons);
  299. var polygonMaximumHeightsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYGON_MAXIMUM_HEIGHTS.byteOffset;
  300. polygonMaximumHeights = new Float32Array(featureTableBinary.buffer, polygonMaximumHeightsByteOffset, numberOfPolygons);
  301. }
  302. content._polygons = new Vector3DTilePolygons({
  303. positions : polygonPositions,
  304. counts : counts,
  305. indexCounts : indexCounts,
  306. indices : indices,
  307. minimumHeight : minHeight,
  308. maximumHeight : maxHeight,
  309. polygonMinimumHeights : polygonMinimumHeights,
  310. polygonMaximumHeights : polygonMaximumHeights,
  311. center : center,
  312. rectangle : rectangle,
  313. boundingVolume : content.tile.boundingVolume.boundingVolume,
  314. batchTable : batchTable,
  315. batchIds : batchIds.polygons,
  316. modelMatrix : modelMatrix
  317. });
  318. }
  319. if (numberOfPolylines > 0) {
  320. var polylinePositions = new Uint16Array(arrayBuffer, byteOffset, polylinePositionByteLength / sizeOfUint16);
  321. byteOffset += polylinePositionByteLength;
  322. var polylineCountByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYLINE_COUNT.byteOffset;
  323. var polylineCounts = new Uint32Array(featureTableBinary.buffer, polylineCountByteOffset, numberOfPolylines);
  324. var widths;
  325. if (!defined(featureTableJson.POLYLINE_WIDTHS)) {
  326. widths = new Uint16Array(numberOfPolylines);
  327. for (var i = 0; i < numberOfPolylines; ++i) {
  328. widths[i] = 2.0;
  329. }
  330. } else {
  331. var polylineWidthsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYLINE_WIDTHS.byteOffset;
  332. widths = new Uint16Array(featureTableBinary.buffer, polylineWidthsByteOffset, numberOfPolylines);
  333. }
  334. content._polylines = new Vector3DTilePolylines({
  335. positions : polylinePositions,
  336. widths : widths,
  337. counts : polylineCounts,
  338. batchIds : batchIds.polylines,
  339. minimumHeight : minHeight,
  340. maximumHeight : maxHeight,
  341. center : center,
  342. rectangle : rectangle,
  343. boundingVolume : content.tile.boundingVolume.boundingVolume,
  344. batchTable : batchTable
  345. });
  346. }
  347. if (numberOfPoints > 0) {
  348. var pointPositions = new Uint16Array(arrayBuffer, byteOffset, pointsPositionByteLength / sizeOfUint16);
  349. content._points = new Vector3DTilePoints({
  350. positions : pointPositions,
  351. batchIds : batchIds.points,
  352. minimumHeight : minHeight,
  353. maximumHeight : maxHeight,
  354. rectangle : rectangle,
  355. batchTable : batchTable
  356. });
  357. }
  358. }
  359. function createFeatures(content) {
  360. var featuresLength = content.featuresLength;
  361. if (!defined(content._features) && (featuresLength > 0)) {
  362. var features = new Array(featuresLength);
  363. if (defined(content._polygons)) {
  364. content._polygons.createFeatures(content, features);
  365. }
  366. if (defined(content._polylines)) {
  367. content._polylines.createFeatures(content, features);
  368. }
  369. if (defined(content._points)) {
  370. content._points.createFeatures(content, features);
  371. }
  372. content._features = features;
  373. }
  374. }
  375. Vector3DTileContent.prototype.hasProperty = function(batchId, name) {
  376. return this._batchTable.hasProperty(batchId, name);
  377. };
  378. Vector3DTileContent.prototype.getFeature = function(batchId) {
  379. //>>includeStart('debug', pragmas.debug);
  380. var featuresLength = this.featuresLength;
  381. if (!defined(batchId) || (batchId < 0) || (batchId >= featuresLength)) {
  382. throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + (featuresLength - 1) + ').');
  383. }
  384. //>>includeEnd('debug');
  385. createFeatures(this);
  386. return this._features[batchId];
  387. };
  388. Vector3DTileContent.prototype.applyDebugSettings = function(enabled, color) {
  389. if (defined(this._polygons)) {
  390. this._polygons.applyDebugSettings(enabled, color);
  391. }
  392. if (defined(this._polylines)) {
  393. this._polylines.applyDebugSettings(enabled, color);
  394. }
  395. if (defined(this._points)) {
  396. this._points.applyDebugSettings(enabled, color);
  397. }
  398. };
  399. Vector3DTileContent.prototype.applyStyle = function(style) {
  400. createFeatures(this);
  401. if (defined(this._polygons)) {
  402. this._polygons.applyStyle(style, this._features);
  403. }
  404. if (defined(this._polylines)) {
  405. this._polylines.applyStyle(style, this._features);
  406. }
  407. if (defined(this._points)) {
  408. this._points.applyStyle(style, this._features);
  409. }
  410. };
  411. Vector3DTileContent.prototype.update = function(tileset, frameState) {
  412. var ready = true;
  413. if (defined(this._polygons)) {
  414. this._polygons.classificationType = this._tileset.classificationType;
  415. this._polygons.debugWireframe = this._tileset.debugWireframe;
  416. this._polygons.update(frameState);
  417. ready = ready && this._polygons._ready;
  418. }
  419. if (defined(this._polylines)) {
  420. this._polylines.update(frameState);
  421. ready = ready && this._polylines._ready;
  422. }
  423. if (defined(this._points)) {
  424. this._points.update(frameState);
  425. ready = ready && this._points._ready;
  426. }
  427. if (defined(this._batchTable) && ready) {
  428. this._batchTable.update(tileset, frameState);
  429. }
  430. if (!defined(this._contentReadyPromise)) {
  431. var pointsPromise = defined(this._points) ? this._points.readyPromise : undefined;
  432. var polygonPromise = defined(this._polygons) ? this._polygons.readyPromise : undefined;
  433. var polylinePromise = defined(this._polylines) ? this._polylines.readyPromise : undefined;
  434. var that = this;
  435. this._contentReadyPromise = when.all([pointsPromise, polygonPromise, polylinePromise]).then(function() {
  436. that._readyPromise.resolve(that);
  437. });
  438. }
  439. };
  440. Vector3DTileContent.prototype.isDestroyed = function() {
  441. return false;
  442. };
  443. Vector3DTileContent.prototype.destroy = function() {
  444. this._polygons = this._polygons && this._polygons.destroy();
  445. this._polylines = this._polylines && this._polylines.destroy();
  446. this._points = this._points && this._points.destroy();
  447. this._batchTable = this._batchTable && this._batchTable.destroy();
  448. return destroyObject(this);
  449. };
  450. export default Vector3DTileContent;