createVerticesFromQuantizedTerrainMesh.js 14 KB

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