createVectorTilePolygons.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. import AttributeCompression from "../Core/AttributeCompression.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartographic from "../Core/Cartographic.js";
  4. import Color from "../Core/Color.js";
  5. import defined from "../Core/defined.js";
  6. import Ellipsoid from "../Core/Ellipsoid.js";
  7. import IndexDatatype from "../Core/IndexDatatype.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  10. import Rectangle from "../Core/Rectangle.js";
  11. import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
  12. const scratchCenter = new Cartesian3();
  13. const scratchEllipsoid = new Ellipsoid();
  14. const scratchRectangle = new Rectangle();
  15. const scratchScalars = {
  16. min: undefined,
  17. max: undefined,
  18. indexBytesPerElement: undefined,
  19. };
  20. function unpackBuffer(buffer) {
  21. const packedBuffer = new Float64Array(buffer);
  22. let offset = 0;
  23. scratchScalars.indexBytesPerElement = packedBuffer[offset++];
  24. scratchScalars.min = packedBuffer[offset++];
  25. scratchScalars.max = packedBuffer[offset++];
  26. Cartesian3.unpack(packedBuffer, offset, scratchCenter);
  27. offset += Cartesian3.packedLength;
  28. Ellipsoid.unpack(packedBuffer, offset, scratchEllipsoid);
  29. offset += Ellipsoid.packedLength;
  30. Rectangle.unpack(packedBuffer, offset, scratchRectangle);
  31. }
  32. function packedBatchedIndicesLength(batchedIndices) {
  33. const length = batchedIndices.length;
  34. let count = 0;
  35. for (let i = 0; i < length; ++i) {
  36. count += Color.packedLength + 3 + batchedIndices[i].batchIds.length;
  37. }
  38. return count;
  39. }
  40. function packBuffer(indexDatatype, boundingVolumes, batchedIndices) {
  41. const numBVs = boundingVolumes.length;
  42. const length =
  43. 1 +
  44. 1 +
  45. numBVs * OrientedBoundingBox.packedLength +
  46. 1 +
  47. packedBatchedIndicesLength(batchedIndices);
  48. const packedBuffer = new Float64Array(length);
  49. let offset = 0;
  50. packedBuffer[offset++] = indexDatatype;
  51. packedBuffer[offset++] = numBVs;
  52. for (let i = 0; i < numBVs; ++i) {
  53. OrientedBoundingBox.pack(boundingVolumes[i], packedBuffer, offset);
  54. offset += OrientedBoundingBox.packedLength;
  55. }
  56. const indicesLength = batchedIndices.length;
  57. packedBuffer[offset++] = indicesLength;
  58. for (let j = 0; j < indicesLength; ++j) {
  59. const batchedIndex = batchedIndices[j];
  60. Color.pack(batchedIndex.color, packedBuffer, offset);
  61. offset += Color.packedLength;
  62. packedBuffer[offset++] = batchedIndex.offset;
  63. packedBuffer[offset++] = batchedIndex.count;
  64. const batchIds = batchedIndex.batchIds;
  65. const batchIdsLength = batchIds.length;
  66. packedBuffer[offset++] = batchIdsLength;
  67. for (let k = 0; k < batchIdsLength; ++k) {
  68. packedBuffer[offset++] = batchIds[k];
  69. }
  70. }
  71. return packedBuffer;
  72. }
  73. const maxShort = 32767;
  74. const scratchEncodedPosition = new Cartesian3();
  75. const scratchNormal = new Cartesian3();
  76. const scratchScaledNormal = new Cartesian3();
  77. const scratchMinHeightPosition = new Cartesian3();
  78. const scratchMaxHeightPosition = new Cartesian3();
  79. const scratchBVCartographic = new Cartographic();
  80. const scratchBVRectangle = new Rectangle();
  81. function createVectorTilePolygons(parameters, transferableObjects) {
  82. unpackBuffer(parameters.packedBuffer);
  83. let indices;
  84. const indexBytesPerElement = scratchScalars.indexBytesPerElement;
  85. if (indexBytesPerElement === 2) {
  86. indices = new Uint16Array(parameters.indices);
  87. } else {
  88. indices = new Uint32Array(parameters.indices);
  89. }
  90. const positions = new Uint16Array(parameters.positions);
  91. const counts = new Uint32Array(parameters.counts);
  92. const indexCounts = new Uint32Array(parameters.indexCounts);
  93. const batchIds = new Uint32Array(parameters.batchIds);
  94. const batchTableColors = new Uint32Array(parameters.batchTableColors);
  95. const boundingVolumes = new Array(counts.length);
  96. const center = scratchCenter;
  97. const ellipsoid = scratchEllipsoid;
  98. let rectangle = scratchRectangle;
  99. const minHeight = scratchScalars.min;
  100. const maxHeight = scratchScalars.max;
  101. let minimumHeights = parameters.minimumHeights;
  102. let maximumHeights = parameters.maximumHeights;
  103. if (defined(minimumHeights) && defined(maximumHeights)) {
  104. minimumHeights = new Float32Array(minimumHeights);
  105. maximumHeights = new Float32Array(maximumHeights);
  106. }
  107. let i;
  108. let j;
  109. let rgba;
  110. const positionsLength = positions.length / 2;
  111. const uBuffer = positions.subarray(0, positionsLength);
  112. const vBuffer = positions.subarray(positionsLength, 2 * positionsLength);
  113. AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer);
  114. const decodedPositions = new Float64Array(positionsLength * 3);
  115. for (i = 0; i < positionsLength; ++i) {
  116. const u = uBuffer[i];
  117. const v = vBuffer[i];
  118. const x = CesiumMath.lerp(rectangle.west, rectangle.east, u / maxShort);
  119. const y = CesiumMath.lerp(rectangle.south, rectangle.north, v / maxShort);
  120. const cart = Cartographic.fromRadians(x, y, 0.0, scratchBVCartographic);
  121. const decodedPosition = ellipsoid.cartographicToCartesian(
  122. cart,
  123. scratchEncodedPosition
  124. );
  125. Cartesian3.pack(decodedPosition, decodedPositions, i * 3);
  126. }
  127. const countsLength = counts.length;
  128. const offsets = new Array(countsLength);
  129. const indexOffsets = new Array(countsLength);
  130. let currentOffset = 0;
  131. let currentIndexOffset = 0;
  132. for (i = 0; i < countsLength; ++i) {
  133. offsets[i] = currentOffset;
  134. indexOffsets[i] = currentIndexOffset;
  135. currentOffset += counts[i];
  136. currentIndexOffset += indexCounts[i];
  137. }
  138. const batchedPositions = new Float32Array(positionsLength * 3 * 2);
  139. const batchedIds = new Uint16Array(positionsLength * 2);
  140. const batchedIndexOffsets = new Uint32Array(indexOffsets.length);
  141. const batchedIndexCounts = new Uint32Array(indexCounts.length);
  142. let batchedIndices = [];
  143. const colorToBuffers = {};
  144. for (i = 0; i < countsLength; ++i) {
  145. rgba = batchTableColors[i];
  146. if (!defined(colorToBuffers[rgba])) {
  147. colorToBuffers[rgba] = {
  148. positionLength: counts[i],
  149. indexLength: indexCounts[i],
  150. offset: 0,
  151. indexOffset: 0,
  152. batchIds: [i],
  153. };
  154. } else {
  155. colorToBuffers[rgba].positionLength += counts[i];
  156. colorToBuffers[rgba].indexLength += indexCounts[i];
  157. colorToBuffers[rgba].batchIds.push(i);
  158. }
  159. }
  160. // get the offsets and counts for the positions and indices of each primitive
  161. let buffer;
  162. let byColorPositionOffset = 0;
  163. let byColorIndexOffset = 0;
  164. for (rgba in colorToBuffers) {
  165. if (colorToBuffers.hasOwnProperty(rgba)) {
  166. buffer = colorToBuffers[rgba];
  167. buffer.offset = byColorPositionOffset;
  168. buffer.indexOffset = byColorIndexOffset;
  169. const positionLength = buffer.positionLength * 2;
  170. const indexLength = buffer.indexLength * 2 + buffer.positionLength * 6;
  171. byColorPositionOffset += positionLength;
  172. byColorIndexOffset += indexLength;
  173. buffer.indexLength = indexLength;
  174. }
  175. }
  176. const batchedDrawCalls = [];
  177. for (rgba in colorToBuffers) {
  178. if (colorToBuffers.hasOwnProperty(rgba)) {
  179. buffer = colorToBuffers[rgba];
  180. batchedDrawCalls.push({
  181. color: Color.fromRgba(parseInt(rgba)),
  182. offset: buffer.indexOffset,
  183. count: buffer.indexLength,
  184. batchIds: buffer.batchIds,
  185. });
  186. }
  187. }
  188. for (i = 0; i < countsLength; ++i) {
  189. rgba = batchTableColors[i];
  190. buffer = colorToBuffers[rgba];
  191. const positionOffset = buffer.offset;
  192. let positionIndex = positionOffset * 3;
  193. let batchIdIndex = positionOffset;
  194. const polygonOffset = offsets[i];
  195. const polygonCount = counts[i];
  196. const batchId = batchIds[i];
  197. let polygonMinimumHeight = minHeight;
  198. let polygonMaximumHeight = maxHeight;
  199. if (defined(minimumHeights) && defined(maximumHeights)) {
  200. polygonMinimumHeight = minimumHeights[i];
  201. polygonMaximumHeight = maximumHeights[i];
  202. }
  203. let minLat = Number.POSITIVE_INFINITY;
  204. let maxLat = Number.NEGATIVE_INFINITY;
  205. let minLon = Number.POSITIVE_INFINITY;
  206. let maxLon = Number.NEGATIVE_INFINITY;
  207. for (j = 0; j < polygonCount; ++j) {
  208. const position = Cartesian3.unpack(
  209. decodedPositions,
  210. polygonOffset * 3 + j * 3,
  211. scratchEncodedPosition
  212. );
  213. ellipsoid.scaleToGeodeticSurface(position, position);
  214. const carto = ellipsoid.cartesianToCartographic(
  215. position,
  216. scratchBVCartographic
  217. );
  218. const lat = carto.latitude;
  219. const lon = carto.longitude;
  220. minLat = Math.min(lat, minLat);
  221. maxLat = Math.max(lat, maxLat);
  222. minLon = Math.min(lon, minLon);
  223. maxLon = Math.max(lon, maxLon);
  224. const normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
  225. let scaledNormal = Cartesian3.multiplyByScalar(
  226. normal,
  227. polygonMinimumHeight,
  228. scratchScaledNormal
  229. );
  230. const minHeightPosition = Cartesian3.add(
  231. position,
  232. scaledNormal,
  233. scratchMinHeightPosition
  234. );
  235. scaledNormal = Cartesian3.multiplyByScalar(
  236. normal,
  237. polygonMaximumHeight,
  238. scaledNormal
  239. );
  240. const maxHeightPosition = Cartesian3.add(
  241. position,
  242. scaledNormal,
  243. scratchMaxHeightPosition
  244. );
  245. Cartesian3.subtract(maxHeightPosition, center, maxHeightPosition);
  246. Cartesian3.subtract(minHeightPosition, center, minHeightPosition);
  247. Cartesian3.pack(maxHeightPosition, batchedPositions, positionIndex);
  248. Cartesian3.pack(minHeightPosition, batchedPositions, positionIndex + 3);
  249. batchedIds[batchIdIndex] = batchId;
  250. batchedIds[batchIdIndex + 1] = batchId;
  251. positionIndex += 6;
  252. batchIdIndex += 2;
  253. }
  254. rectangle = scratchBVRectangle;
  255. rectangle.west = minLon;
  256. rectangle.east = maxLon;
  257. rectangle.south = minLat;
  258. rectangle.north = maxLat;
  259. boundingVolumes[i] = OrientedBoundingBox.fromRectangle(
  260. rectangle,
  261. minHeight,
  262. maxHeight,
  263. ellipsoid
  264. );
  265. let indicesIndex = buffer.indexOffset;
  266. const indexOffset = indexOffsets[i];
  267. const indexCount = indexCounts[i];
  268. batchedIndexOffsets[i] = indicesIndex;
  269. for (j = 0; j < indexCount; j += 3) {
  270. const i0 = indices[indexOffset + j] - polygonOffset;
  271. const i1 = indices[indexOffset + j + 1] - polygonOffset;
  272. const i2 = indices[indexOffset + j + 2] - polygonOffset;
  273. // triangle on the top of the extruded polygon
  274. batchedIndices[indicesIndex++] = i0 * 2 + positionOffset;
  275. batchedIndices[indicesIndex++] = i1 * 2 + positionOffset;
  276. batchedIndices[indicesIndex++] = i2 * 2 + positionOffset;
  277. // triangle on the bottom of the extruded polygon
  278. batchedIndices[indicesIndex++] = i2 * 2 + 1 + positionOffset;
  279. batchedIndices[indicesIndex++] = i1 * 2 + 1 + positionOffset;
  280. batchedIndices[indicesIndex++] = i0 * 2 + 1 + positionOffset;
  281. }
  282. // indices for the walls of the extruded polygon
  283. for (j = 0; j < polygonCount; ++j) {
  284. const v0 = j;
  285. const v1 = (j + 1) % polygonCount;
  286. batchedIndices[indicesIndex++] = v0 * 2 + 1 + positionOffset;
  287. batchedIndices[indicesIndex++] = v1 * 2 + positionOffset;
  288. batchedIndices[indicesIndex++] = v0 * 2 + positionOffset;
  289. batchedIndices[indicesIndex++] = v0 * 2 + 1 + positionOffset;
  290. batchedIndices[indicesIndex++] = v1 * 2 + 1 + positionOffset;
  291. batchedIndices[indicesIndex++] = v1 * 2 + positionOffset;
  292. }
  293. buffer.offset += polygonCount * 2;
  294. buffer.indexOffset = indicesIndex;
  295. batchedIndexCounts[i] = indicesIndex - batchedIndexOffsets[i];
  296. }
  297. batchedIndices = IndexDatatype.createTypedArray(
  298. batchedPositions.length / 3,
  299. batchedIndices
  300. );
  301. const batchedIndicesLength = batchedDrawCalls.length;
  302. for (let m = 0; m < batchedIndicesLength; ++m) {
  303. const tempIds = batchedDrawCalls[m].batchIds;
  304. let count = 0;
  305. const tempIdsLength = tempIds.length;
  306. for (let n = 0; n < tempIdsLength; ++n) {
  307. count += batchedIndexCounts[tempIds[n]];
  308. }
  309. batchedDrawCalls[m].count = count;
  310. }
  311. const indexDatatype =
  312. batchedIndices.BYTES_PER_ELEMENT === 2
  313. ? IndexDatatype.UNSIGNED_SHORT
  314. : IndexDatatype.UNSIGNED_INT;
  315. const packedBuffer = packBuffer(
  316. indexDatatype,
  317. boundingVolumes,
  318. batchedDrawCalls
  319. );
  320. transferableObjects.push(
  321. batchedPositions.buffer,
  322. batchedIndices.buffer,
  323. batchedIndexOffsets.buffer,
  324. batchedIndexCounts.buffer,
  325. batchedIds.buffer,
  326. packedBuffer.buffer
  327. );
  328. return {
  329. positions: batchedPositions.buffer,
  330. indices: batchedIndices.buffer,
  331. indexOffsets: batchedIndexOffsets.buffer,
  332. indexCounts: batchedIndexCounts.buffer,
  333. batchIds: batchedIds.buffer,
  334. packedBuffer: packedBuffer.buffer,
  335. };
  336. }
  337. export default createTaskProcessorWorker(createVectorTilePolygons);