createVerticesFromQuantizedTerrainMesh.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. import AttributeCompression from "../Core/AttributeCompression.js";
  2. import AxisAlignedBoundingBox from "../Core/AxisAlignedBoundingBox.js";
  3. import BoundingSphere from "../Core/BoundingSphere.js";
  4. import Cartesian2 from "../Core/Cartesian2.js";
  5. import Cartesian3 from "../Core/Cartesian3.js";
  6. import Cartographic from "../Core/Cartographic.js";
  7. import defined from "../Core/defined.js";
  8. import Ellipsoid from "../Core/Ellipsoid.js";
  9. import EllipsoidalOccluder from "../Core/EllipsoidalOccluder.js";
  10. import IndexDatatype from "../Core/IndexDatatype.js";
  11. import CesiumMath from "../Core/Math.js";
  12. import Matrix4 from "../Core/Matrix4.js";
  13. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  14. import Rectangle from "../Core/Rectangle.js";
  15. import TerrainEncoding from "../Core/TerrainEncoding.js";
  16. import TerrainProvider from "../Core/TerrainProvider.js";
  17. import Transforms from "../Core/Transforms.js";
  18. import WebMercatorProjection from "../Core/WebMercatorProjection.js";
  19. import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
  20. var maxShort = 32767;
  21. var cartesian3Scratch = new Cartesian3();
  22. var scratchMinimum = new Cartesian3();
  23. var scratchMaximum = new Cartesian3();
  24. var cartographicScratch = new Cartographic();
  25. var toPack = new Cartesian2();
  26. var scratchNormal = new Cartesian3();
  27. var scratchToENU = new Matrix4();
  28. var scratchFromENU = new Matrix4();
  29. function createVerticesFromQuantizedTerrainMesh(
  30. parameters,
  31. transferableObjects
  32. ) {
  33. var quantizedVertices = parameters.quantizedVertices;
  34. var quantizedVertexCount = quantizedVertices.length / 3;
  35. var octEncodedNormals = parameters.octEncodedNormals;
  36. var edgeVertexCount =
  37. parameters.westIndices.length +
  38. parameters.eastIndices.length +
  39. parameters.southIndices.length +
  40. parameters.northIndices.length;
  41. var includeWebMercatorT = parameters.includeWebMercatorT;
  42. var rectangle = Rectangle.clone(parameters.rectangle);
  43. var west = rectangle.west;
  44. var south = rectangle.south;
  45. var east = rectangle.east;
  46. var north = rectangle.north;
  47. var ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
  48. var exaggeration = parameters.exaggeration;
  49. var minimumHeight = parameters.minimumHeight * exaggeration;
  50. var maximumHeight = parameters.maximumHeight * exaggeration;
  51. var center = parameters.relativeToCenter;
  52. var fromENU = Transforms.eastNorthUpToFixedFrame(center, ellipsoid);
  53. var toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());
  54. var southMercatorY;
  55. var oneOverMercatorHeight;
  56. if (includeWebMercatorT) {
  57. southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  58. south
  59. );
  60. oneOverMercatorHeight =
  61. 1.0 /
  62. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(north) -
  63. southMercatorY);
  64. }
  65. var uBuffer = quantizedVertices.subarray(0, quantizedVertexCount);
  66. var vBuffer = quantizedVertices.subarray(
  67. quantizedVertexCount,
  68. 2 * quantizedVertexCount
  69. );
  70. var heightBuffer = quantizedVertices.subarray(
  71. quantizedVertexCount * 2,
  72. 3 * quantizedVertexCount
  73. );
  74. var hasVertexNormals = defined(octEncodedNormals);
  75. var uvs = new Array(quantizedVertexCount);
  76. var heights = new Array(quantizedVertexCount);
  77. var positions = new Array(quantizedVertexCount);
  78. var webMercatorTs = includeWebMercatorT
  79. ? new Array(quantizedVertexCount)
  80. : [];
  81. var minimum = scratchMinimum;
  82. minimum.x = Number.POSITIVE_INFINITY;
  83. minimum.y = Number.POSITIVE_INFINITY;
  84. minimum.z = Number.POSITIVE_INFINITY;
  85. var maximum = scratchMaximum;
  86. maximum.x = Number.NEGATIVE_INFINITY;
  87. maximum.y = Number.NEGATIVE_INFINITY;
  88. maximum.z = Number.NEGATIVE_INFINITY;
  89. var minLongitude = Number.POSITIVE_INFINITY;
  90. var maxLongitude = Number.NEGATIVE_INFINITY;
  91. var minLatitude = Number.POSITIVE_INFINITY;
  92. var maxLatitude = Number.NEGATIVE_INFINITY;
  93. for (var i = 0; i < quantizedVertexCount; ++i) {
  94. var rawU = uBuffer[i];
  95. var rawV = vBuffer[i];
  96. var u = rawU / maxShort;
  97. var v = rawV / maxShort;
  98. var height = CesiumMath.lerp(
  99. minimumHeight,
  100. maximumHeight,
  101. heightBuffer[i] / maxShort
  102. );
  103. cartographicScratch.longitude = CesiumMath.lerp(west, east, u);
  104. cartographicScratch.latitude = CesiumMath.lerp(south, north, v);
  105. cartographicScratch.height = height;
  106. minLongitude = Math.min(cartographicScratch.longitude, minLongitude);
  107. maxLongitude = Math.max(cartographicScratch.longitude, maxLongitude);
  108. minLatitude = Math.min(cartographicScratch.latitude, minLatitude);
  109. maxLatitude = Math.max(cartographicScratch.latitude, maxLatitude);
  110. var position = ellipsoid.cartographicToCartesian(cartographicScratch);
  111. uvs[i] = new Cartesian2(u, v);
  112. heights[i] = height;
  113. positions[i] = position;
  114. if (includeWebMercatorT) {
  115. webMercatorTs[i] =
  116. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  117. cartographicScratch.latitude
  118. ) -
  119. southMercatorY) *
  120. oneOverMercatorHeight;
  121. }
  122. Matrix4.multiplyByPoint(toENU, position, cartesian3Scratch);
  123. Cartesian3.minimumByComponent(cartesian3Scratch, minimum, minimum);
  124. Cartesian3.maximumByComponent(cartesian3Scratch, maximum, maximum);
  125. }
  126. var westIndicesSouthToNorth = copyAndSort(parameters.westIndices, function (
  127. a,
  128. b
  129. ) {
  130. return uvs[a].y - uvs[b].y;
  131. });
  132. var eastIndicesNorthToSouth = copyAndSort(parameters.eastIndices, function (
  133. a,
  134. b
  135. ) {
  136. return uvs[b].y - uvs[a].y;
  137. });
  138. var southIndicesEastToWest = copyAndSort(parameters.southIndices, function (
  139. a,
  140. b
  141. ) {
  142. return uvs[b].x - uvs[a].x;
  143. });
  144. var northIndicesWestToEast = copyAndSort(parameters.northIndices, function (
  145. a,
  146. b
  147. ) {
  148. return uvs[a].x - uvs[b].x;
  149. });
  150. var orientedBoundingBox;
  151. var boundingSphere;
  152. if (exaggeration !== 1.0) {
  153. // Bounding volumes need to be recomputed since the tile payload assumes no exaggeration.
  154. boundingSphere = BoundingSphere.fromPoints(positions);
  155. orientedBoundingBox = OrientedBoundingBox.fromRectangle(
  156. rectangle,
  157. minimumHeight,
  158. maximumHeight,
  159. ellipsoid
  160. );
  161. }
  162. var occludeePointInScaledSpace;
  163. if (exaggeration !== 1.0 || minimumHeight < 0.0) {
  164. // Horizon culling point needs to be recomputed since the tile payload assumes no exaggeration.
  165. var occluder = new EllipsoidalOccluder(ellipsoid);
  166. occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(
  167. center,
  168. positions,
  169. minimumHeight
  170. );
  171. }
  172. var hMin = minimumHeight;
  173. hMin = Math.min(
  174. hMin,
  175. findMinMaxSkirts(
  176. parameters.westIndices,
  177. parameters.westSkirtHeight,
  178. heights,
  179. uvs,
  180. rectangle,
  181. ellipsoid,
  182. toENU,
  183. minimum,
  184. maximum
  185. )
  186. );
  187. hMin = Math.min(
  188. hMin,
  189. findMinMaxSkirts(
  190. parameters.southIndices,
  191. parameters.southSkirtHeight,
  192. heights,
  193. uvs,
  194. rectangle,
  195. ellipsoid,
  196. toENU,
  197. minimum,
  198. maximum
  199. )
  200. );
  201. hMin = Math.min(
  202. hMin,
  203. findMinMaxSkirts(
  204. parameters.eastIndices,
  205. parameters.eastSkirtHeight,
  206. heights,
  207. uvs,
  208. rectangle,
  209. ellipsoid,
  210. toENU,
  211. minimum,
  212. maximum
  213. )
  214. );
  215. hMin = Math.min(
  216. hMin,
  217. findMinMaxSkirts(
  218. parameters.northIndices,
  219. parameters.northSkirtHeight,
  220. heights,
  221. uvs,
  222. rectangle,
  223. ellipsoid,
  224. toENU,
  225. minimum,
  226. maximum
  227. )
  228. );
  229. var aaBox = new AxisAlignedBoundingBox(minimum, maximum, center);
  230. var encoding = new TerrainEncoding(
  231. aaBox,
  232. hMin,
  233. maximumHeight,
  234. fromENU,
  235. hasVertexNormals,
  236. includeWebMercatorT
  237. );
  238. var vertexStride = encoding.getStride();
  239. var size =
  240. quantizedVertexCount * vertexStride + edgeVertexCount * vertexStride;
  241. var vertexBuffer = new Float32Array(size);
  242. var bufferIndex = 0;
  243. for (var j = 0; j < quantizedVertexCount; ++j) {
  244. if (hasVertexNormals) {
  245. var n = j * 2.0;
  246. toPack.x = octEncodedNormals[n];
  247. toPack.y = octEncodedNormals[n + 1];
  248. if (exaggeration !== 1.0) {
  249. var normal = AttributeCompression.octDecode(
  250. toPack.x,
  251. toPack.y,
  252. scratchNormal
  253. );
  254. var fromENUNormal = Transforms.eastNorthUpToFixedFrame(
  255. positions[j],
  256. ellipsoid,
  257. scratchFromENU
  258. );
  259. var toENUNormal = Matrix4.inverseTransformation(
  260. fromENUNormal,
  261. scratchToENU
  262. );
  263. Matrix4.multiplyByPointAsVector(toENUNormal, normal, normal);
  264. normal.z *= exaggeration;
  265. Cartesian3.normalize(normal, normal);
  266. Matrix4.multiplyByPointAsVector(fromENUNormal, normal, normal);
  267. Cartesian3.normalize(normal, normal);
  268. AttributeCompression.octEncode(normal, toPack);
  269. }
  270. }
  271. bufferIndex = encoding.encode(
  272. vertexBuffer,
  273. bufferIndex,
  274. positions[j],
  275. uvs[j],
  276. heights[j],
  277. toPack,
  278. webMercatorTs[j]
  279. );
  280. }
  281. var edgeTriangleCount = Math.max(0, (edgeVertexCount - 4) * 2);
  282. var indexBufferLength = parameters.indices.length + edgeTriangleCount * 3;
  283. var indexBuffer = IndexDatatype.createTypedArray(
  284. quantizedVertexCount + edgeVertexCount,
  285. indexBufferLength
  286. );
  287. indexBuffer.set(parameters.indices, 0);
  288. var percentage = 0.0001;
  289. var lonOffset = (maxLongitude - minLongitude) * percentage;
  290. var latOffset = (maxLatitude - minLatitude) * percentage;
  291. var westLongitudeOffset = -lonOffset;
  292. var westLatitudeOffset = 0.0;
  293. var eastLongitudeOffset = lonOffset;
  294. var eastLatitudeOffset = 0.0;
  295. var northLongitudeOffset = 0.0;
  296. var northLatitudeOffset = latOffset;
  297. var southLongitudeOffset = 0.0;
  298. var southLatitudeOffset = -latOffset;
  299. // Add skirts.
  300. var vertexBufferIndex = quantizedVertexCount * vertexStride;
  301. addSkirt(
  302. vertexBuffer,
  303. vertexBufferIndex,
  304. westIndicesSouthToNorth,
  305. encoding,
  306. heights,
  307. uvs,
  308. octEncodedNormals,
  309. ellipsoid,
  310. rectangle,
  311. parameters.westSkirtHeight,
  312. exaggeration,
  313. southMercatorY,
  314. oneOverMercatorHeight,
  315. westLongitudeOffset,
  316. westLatitudeOffset
  317. );
  318. vertexBufferIndex += parameters.westIndices.length * vertexStride;
  319. addSkirt(
  320. vertexBuffer,
  321. vertexBufferIndex,
  322. southIndicesEastToWest,
  323. encoding,
  324. heights,
  325. uvs,
  326. octEncodedNormals,
  327. ellipsoid,
  328. rectangle,
  329. parameters.southSkirtHeight,
  330. exaggeration,
  331. southMercatorY,
  332. oneOverMercatorHeight,
  333. southLongitudeOffset,
  334. southLatitudeOffset
  335. );
  336. vertexBufferIndex += parameters.southIndices.length * vertexStride;
  337. addSkirt(
  338. vertexBuffer,
  339. vertexBufferIndex,
  340. eastIndicesNorthToSouth,
  341. encoding,
  342. heights,
  343. uvs,
  344. octEncodedNormals,
  345. ellipsoid,
  346. rectangle,
  347. parameters.eastSkirtHeight,
  348. exaggeration,
  349. southMercatorY,
  350. oneOverMercatorHeight,
  351. eastLongitudeOffset,
  352. eastLatitudeOffset
  353. );
  354. vertexBufferIndex += parameters.eastIndices.length * vertexStride;
  355. addSkirt(
  356. vertexBuffer,
  357. vertexBufferIndex,
  358. northIndicesWestToEast,
  359. encoding,
  360. heights,
  361. uvs,
  362. octEncodedNormals,
  363. ellipsoid,
  364. rectangle,
  365. parameters.northSkirtHeight,
  366. exaggeration,
  367. southMercatorY,
  368. oneOverMercatorHeight,
  369. northLongitudeOffset,
  370. northLatitudeOffset
  371. );
  372. TerrainProvider.addSkirtIndices(
  373. westIndicesSouthToNorth,
  374. southIndicesEastToWest,
  375. eastIndicesNorthToSouth,
  376. northIndicesWestToEast,
  377. quantizedVertexCount,
  378. indexBuffer,
  379. parameters.indices.length
  380. );
  381. transferableObjects.push(vertexBuffer.buffer, indexBuffer.buffer);
  382. return {
  383. vertices: vertexBuffer.buffer,
  384. indices: indexBuffer.buffer,
  385. westIndicesSouthToNorth: westIndicesSouthToNorth,
  386. southIndicesEastToWest: southIndicesEastToWest,
  387. eastIndicesNorthToSouth: eastIndicesNorthToSouth,
  388. northIndicesWestToEast: northIndicesWestToEast,
  389. vertexStride: vertexStride,
  390. center: center,
  391. minimumHeight: minimumHeight,
  392. maximumHeight: maximumHeight,
  393. boundingSphere: boundingSphere,
  394. orientedBoundingBox: orientedBoundingBox,
  395. occludeePointInScaledSpace: occludeePointInScaledSpace,
  396. encoding: encoding,
  397. indexCountWithoutSkirts: parameters.indices.length,
  398. };
  399. }
  400. function findMinMaxSkirts(
  401. edgeIndices,
  402. edgeHeight,
  403. heights,
  404. uvs,
  405. rectangle,
  406. ellipsoid,
  407. toENU,
  408. minimum,
  409. maximum
  410. ) {
  411. var hMin = Number.POSITIVE_INFINITY;
  412. var north = rectangle.north;
  413. var south = rectangle.south;
  414. var east = rectangle.east;
  415. var west = rectangle.west;
  416. if (east < west) {
  417. east += CesiumMath.TWO_PI;
  418. }
  419. var length = edgeIndices.length;
  420. for (var i = 0; i < length; ++i) {
  421. var index = edgeIndices[i];
  422. var h = heights[index];
  423. var uv = uvs[index];
  424. cartographicScratch.longitude = CesiumMath.lerp(west, east, uv.x);
  425. cartographicScratch.latitude = CesiumMath.lerp(south, north, uv.y);
  426. cartographicScratch.height = h - edgeHeight;
  427. var position = ellipsoid.cartographicToCartesian(
  428. cartographicScratch,
  429. cartesian3Scratch
  430. );
  431. Matrix4.multiplyByPoint(toENU, position, position);
  432. Cartesian3.minimumByComponent(position, minimum, minimum);
  433. Cartesian3.maximumByComponent(position, maximum, maximum);
  434. hMin = Math.min(hMin, cartographicScratch.height);
  435. }
  436. return hMin;
  437. }
  438. function addSkirt(
  439. vertexBuffer,
  440. vertexBufferIndex,
  441. edgeVertices,
  442. encoding,
  443. heights,
  444. uvs,
  445. octEncodedNormals,
  446. ellipsoid,
  447. rectangle,
  448. skirtLength,
  449. exaggeration,
  450. southMercatorY,
  451. oneOverMercatorHeight,
  452. longitudeOffset,
  453. latitudeOffset
  454. ) {
  455. var hasVertexNormals = defined(octEncodedNormals);
  456. var north = rectangle.north;
  457. var south = rectangle.south;
  458. var east = rectangle.east;
  459. var west = rectangle.west;
  460. if (east < west) {
  461. east += CesiumMath.TWO_PI;
  462. }
  463. var length = edgeVertices.length;
  464. for (var i = 0; i < length; ++i) {
  465. var index = edgeVertices[i];
  466. var h = heights[index];
  467. var uv = uvs[index];
  468. cartographicScratch.longitude =
  469. CesiumMath.lerp(west, east, uv.x) + longitudeOffset;
  470. cartographicScratch.latitude =
  471. CesiumMath.lerp(south, north, uv.y) + latitudeOffset;
  472. cartographicScratch.height = h - skirtLength;
  473. var position = ellipsoid.cartographicToCartesian(
  474. cartographicScratch,
  475. cartesian3Scratch
  476. );
  477. if (hasVertexNormals) {
  478. var n = index * 2.0;
  479. toPack.x = octEncodedNormals[n];
  480. toPack.y = octEncodedNormals[n + 1];
  481. if (exaggeration !== 1.0) {
  482. var normal = AttributeCompression.octDecode(
  483. toPack.x,
  484. toPack.y,
  485. scratchNormal
  486. );
  487. var fromENUNormal = Transforms.eastNorthUpToFixedFrame(
  488. cartesian3Scratch,
  489. ellipsoid,
  490. scratchFromENU
  491. );
  492. var toENUNormal = Matrix4.inverseTransformation(
  493. fromENUNormal,
  494. scratchToENU
  495. );
  496. Matrix4.multiplyByPointAsVector(toENUNormal, normal, normal);
  497. normal.z *= exaggeration;
  498. Cartesian3.normalize(normal, normal);
  499. Matrix4.multiplyByPointAsVector(fromENUNormal, normal, normal);
  500. Cartesian3.normalize(normal, normal);
  501. AttributeCompression.octEncode(normal, toPack);
  502. }
  503. }
  504. var webMercatorT;
  505. if (encoding.hasWebMercatorT) {
  506. webMercatorT =
  507. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  508. cartographicScratch.latitude
  509. ) -
  510. southMercatorY) *
  511. oneOverMercatorHeight;
  512. }
  513. vertexBufferIndex = encoding.encode(
  514. vertexBuffer,
  515. vertexBufferIndex,
  516. position,
  517. uv,
  518. cartographicScratch.height,
  519. toPack,
  520. webMercatorT
  521. );
  522. }
  523. }
  524. function copyAndSort(typedArray, comparator) {
  525. var copy;
  526. if (typeof typedArray.slice === "function") {
  527. copy = typedArray.slice();
  528. if (typeof copy.sort !== "function") {
  529. // Sliced typed array isn't sortable, so we can't use it.
  530. copy = undefined;
  531. }
  532. }
  533. if (!defined(copy)) {
  534. copy = Array.prototype.slice.call(typedArray);
  535. }
  536. copy.sort(comparator);
  537. return copy;
  538. }
  539. export default createTaskProcessorWorker(
  540. createVerticesFromQuantizedTerrainMesh
  541. );