upsampleQuantizedTerrainMesh.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. import AttributeCompression from "../Core/AttributeCompression.js";
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Cartesian2 from "../Core/Cartesian2.js";
  4. import Cartesian3 from "../Core/Cartesian3.js";
  5. import Cartographic from "../Core/Cartographic.js";
  6. import defined from "../Core/defined.js";
  7. import Ellipsoid from "../Core/Ellipsoid.js";
  8. import EllipsoidalOccluder from "../Core/EllipsoidalOccluder.js";
  9. import IndexDatatype from "../Core/IndexDatatype.js";
  10. import Intersections2D from "../Core/Intersections2D.js";
  11. import CesiumMath from "../Core/Math.js";
  12. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  13. import Rectangle from "../Core/Rectangle.js";
  14. import TerrainEncoding from "../Core/TerrainEncoding.js";
  15. import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
  16. var maxShort = 32767;
  17. var halfMaxShort = (maxShort / 2) | 0;
  18. var clipScratch = [];
  19. var clipScratch2 = [];
  20. var verticesScratch = [];
  21. var cartographicScratch = new Cartographic();
  22. var cartesian3Scratch = new Cartesian3();
  23. var uScratch = [];
  24. var vScratch = [];
  25. var heightScratch = [];
  26. var indicesScratch = [];
  27. var normalsScratch = [];
  28. var horizonOcclusionPointScratch = new Cartesian3();
  29. var boundingSphereScratch = new BoundingSphere();
  30. var orientedBoundingBoxScratch = new OrientedBoundingBox();
  31. var decodeTexCoordsScratch = new Cartesian2();
  32. var octEncodedNormalScratch = new Cartesian3();
  33. function upsampleQuantizedTerrainMesh(parameters, transferableObjects) {
  34. var isEastChild = parameters.isEastChild;
  35. var isNorthChild = parameters.isNorthChild;
  36. var minU = isEastChild ? halfMaxShort : 0;
  37. var maxU = isEastChild ? maxShort : halfMaxShort;
  38. var minV = isNorthChild ? halfMaxShort : 0;
  39. var maxV = isNorthChild ? maxShort : halfMaxShort;
  40. var uBuffer = uScratch;
  41. var vBuffer = vScratch;
  42. var heightBuffer = heightScratch;
  43. var normalBuffer = normalsScratch;
  44. uBuffer.length = 0;
  45. vBuffer.length = 0;
  46. heightBuffer.length = 0;
  47. normalBuffer.length = 0;
  48. var indices = indicesScratch;
  49. indices.length = 0;
  50. var vertexMap = {};
  51. var parentVertices = parameters.vertices;
  52. var parentIndices = parameters.indices;
  53. parentIndices = parentIndices.subarray(0, parameters.indexCountWithoutSkirts);
  54. var encoding = TerrainEncoding.clone(parameters.encoding);
  55. var hasVertexNormals = encoding.hasVertexNormals;
  56. var exaggeration = parameters.exaggeration;
  57. var vertexCount = 0;
  58. var quantizedVertexCount = parameters.vertexCountWithoutSkirts;
  59. var parentMinimumHeight = parameters.minimumHeight;
  60. var parentMaximumHeight = parameters.maximumHeight;
  61. var parentUBuffer = new Array(quantizedVertexCount);
  62. var parentVBuffer = new Array(quantizedVertexCount);
  63. var parentHeightBuffer = new Array(quantizedVertexCount);
  64. var parentNormalBuffer = hasVertexNormals
  65. ? new Array(quantizedVertexCount * 2)
  66. : undefined;
  67. var threshold = 20;
  68. var height;
  69. var i, n;
  70. var u, v;
  71. for (i = 0, n = 0; i < quantizedVertexCount; ++i, n += 2) {
  72. var texCoords = encoding.decodeTextureCoordinates(
  73. parentVertices,
  74. i,
  75. decodeTexCoordsScratch
  76. );
  77. height = encoding.decodeHeight(parentVertices, i) / exaggeration;
  78. u = CesiumMath.clamp((texCoords.x * maxShort) | 0, 0, maxShort);
  79. v = CesiumMath.clamp((texCoords.y * maxShort) | 0, 0, maxShort);
  80. parentHeightBuffer[i] = CesiumMath.clamp(
  81. (((height - parentMinimumHeight) /
  82. (parentMaximumHeight - parentMinimumHeight)) *
  83. maxShort) |
  84. 0,
  85. 0,
  86. maxShort
  87. );
  88. if (u < threshold) {
  89. u = 0;
  90. }
  91. if (v < threshold) {
  92. v = 0;
  93. }
  94. if (maxShort - u < threshold) {
  95. u = maxShort;
  96. }
  97. if (maxShort - v < threshold) {
  98. v = maxShort;
  99. }
  100. parentUBuffer[i] = u;
  101. parentVBuffer[i] = v;
  102. if (hasVertexNormals) {
  103. var encodedNormal = encoding.getOctEncodedNormal(
  104. parentVertices,
  105. i,
  106. octEncodedNormalScratch
  107. );
  108. parentNormalBuffer[n] = encodedNormal.x;
  109. parentNormalBuffer[n + 1] = encodedNormal.y;
  110. }
  111. if (
  112. ((isEastChild && u >= halfMaxShort) ||
  113. (!isEastChild && u <= halfMaxShort)) &&
  114. ((isNorthChild && v >= halfMaxShort) ||
  115. (!isNorthChild && v <= halfMaxShort))
  116. ) {
  117. vertexMap[i] = vertexCount;
  118. uBuffer.push(u);
  119. vBuffer.push(v);
  120. heightBuffer.push(parentHeightBuffer[i]);
  121. if (hasVertexNormals) {
  122. normalBuffer.push(parentNormalBuffer[n]);
  123. normalBuffer.push(parentNormalBuffer[n + 1]);
  124. }
  125. ++vertexCount;
  126. }
  127. }
  128. var triangleVertices = [];
  129. triangleVertices.push(new Vertex());
  130. triangleVertices.push(new Vertex());
  131. triangleVertices.push(new Vertex());
  132. var clippedTriangleVertices = [];
  133. clippedTriangleVertices.push(new Vertex());
  134. clippedTriangleVertices.push(new Vertex());
  135. clippedTriangleVertices.push(new Vertex());
  136. var clippedIndex;
  137. var clipped2;
  138. for (i = 0; i < parentIndices.length; i += 3) {
  139. var i0 = parentIndices[i];
  140. var i1 = parentIndices[i + 1];
  141. var i2 = parentIndices[i + 2];
  142. var u0 = parentUBuffer[i0];
  143. var u1 = parentUBuffer[i1];
  144. var u2 = parentUBuffer[i2];
  145. triangleVertices[0].initializeIndexed(
  146. parentUBuffer,
  147. parentVBuffer,
  148. parentHeightBuffer,
  149. parentNormalBuffer,
  150. i0
  151. );
  152. triangleVertices[1].initializeIndexed(
  153. parentUBuffer,
  154. parentVBuffer,
  155. parentHeightBuffer,
  156. parentNormalBuffer,
  157. i1
  158. );
  159. triangleVertices[2].initializeIndexed(
  160. parentUBuffer,
  161. parentVBuffer,
  162. parentHeightBuffer,
  163. parentNormalBuffer,
  164. i2
  165. );
  166. // Clip triangle on the east-west boundary.
  167. var clipped = Intersections2D.clipTriangleAtAxisAlignedThreshold(
  168. halfMaxShort,
  169. isEastChild,
  170. u0,
  171. u1,
  172. u2,
  173. clipScratch
  174. );
  175. // Get the first clipped triangle, if any.
  176. clippedIndex = 0;
  177. if (clippedIndex >= clipped.length) {
  178. continue;
  179. }
  180. clippedIndex = clippedTriangleVertices[0].initializeFromClipResult(
  181. clipped,
  182. clippedIndex,
  183. triangleVertices
  184. );
  185. if (clippedIndex >= clipped.length) {
  186. continue;
  187. }
  188. clippedIndex = clippedTriangleVertices[1].initializeFromClipResult(
  189. clipped,
  190. clippedIndex,
  191. triangleVertices
  192. );
  193. if (clippedIndex >= clipped.length) {
  194. continue;
  195. }
  196. clippedIndex = clippedTriangleVertices[2].initializeFromClipResult(
  197. clipped,
  198. clippedIndex,
  199. triangleVertices
  200. );
  201. // Clip the triangle against the North-south boundary.
  202. clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(
  203. halfMaxShort,
  204. isNorthChild,
  205. clippedTriangleVertices[0].getV(),
  206. clippedTriangleVertices[1].getV(),
  207. clippedTriangleVertices[2].getV(),
  208. clipScratch2
  209. );
  210. addClippedPolygon(
  211. uBuffer,
  212. vBuffer,
  213. heightBuffer,
  214. normalBuffer,
  215. indices,
  216. vertexMap,
  217. clipped2,
  218. clippedTriangleVertices,
  219. hasVertexNormals
  220. );
  221. // If there's another vertex in the original clipped result,
  222. // it forms a second triangle. Clip it as well.
  223. if (clippedIndex < clipped.length) {
  224. clippedTriangleVertices[2].clone(clippedTriangleVertices[1]);
  225. clippedTriangleVertices[2].initializeFromClipResult(
  226. clipped,
  227. clippedIndex,
  228. triangleVertices
  229. );
  230. clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(
  231. halfMaxShort,
  232. isNorthChild,
  233. clippedTriangleVertices[0].getV(),
  234. clippedTriangleVertices[1].getV(),
  235. clippedTriangleVertices[2].getV(),
  236. clipScratch2
  237. );
  238. addClippedPolygon(
  239. uBuffer,
  240. vBuffer,
  241. heightBuffer,
  242. normalBuffer,
  243. indices,
  244. vertexMap,
  245. clipped2,
  246. clippedTriangleVertices,
  247. hasVertexNormals
  248. );
  249. }
  250. }
  251. var uOffset = isEastChild ? -maxShort : 0;
  252. var vOffset = isNorthChild ? -maxShort : 0;
  253. var westIndices = [];
  254. var southIndices = [];
  255. var eastIndices = [];
  256. var northIndices = [];
  257. var minimumHeight = Number.MAX_VALUE;
  258. var maximumHeight = -minimumHeight;
  259. var cartesianVertices = verticesScratch;
  260. cartesianVertices.length = 0;
  261. var ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
  262. var rectangle = Rectangle.clone(parameters.childRectangle);
  263. var north = rectangle.north;
  264. var south = rectangle.south;
  265. var east = rectangle.east;
  266. var west = rectangle.west;
  267. if (east < west) {
  268. east += CesiumMath.TWO_PI;
  269. }
  270. for (i = 0; i < uBuffer.length; ++i) {
  271. u = Math.round(uBuffer[i]);
  272. if (u <= minU) {
  273. westIndices.push(i);
  274. u = 0;
  275. } else if (u >= maxU) {
  276. eastIndices.push(i);
  277. u = maxShort;
  278. } else {
  279. u = u * 2 + uOffset;
  280. }
  281. uBuffer[i] = u;
  282. v = Math.round(vBuffer[i]);
  283. if (v <= minV) {
  284. southIndices.push(i);
  285. v = 0;
  286. } else if (v >= maxV) {
  287. northIndices.push(i);
  288. v = maxShort;
  289. } else {
  290. v = v * 2 + vOffset;
  291. }
  292. vBuffer[i] = v;
  293. height = CesiumMath.lerp(
  294. parentMinimumHeight,
  295. parentMaximumHeight,
  296. heightBuffer[i] / maxShort
  297. );
  298. if (height < minimumHeight) {
  299. minimumHeight = height;
  300. }
  301. if (height > maximumHeight) {
  302. maximumHeight = height;
  303. }
  304. heightBuffer[i] = height;
  305. cartographicScratch.longitude = CesiumMath.lerp(west, east, u / maxShort);
  306. cartographicScratch.latitude = CesiumMath.lerp(south, north, v / maxShort);
  307. cartographicScratch.height = height;
  308. ellipsoid.cartographicToCartesian(cartographicScratch, cartesian3Scratch);
  309. cartesianVertices.push(cartesian3Scratch.x);
  310. cartesianVertices.push(cartesian3Scratch.y);
  311. cartesianVertices.push(cartesian3Scratch.z);
  312. }
  313. var boundingSphere = BoundingSphere.fromVertices(
  314. cartesianVertices,
  315. Cartesian3.ZERO,
  316. 3,
  317. boundingSphereScratch
  318. );
  319. var orientedBoundingBox = OrientedBoundingBox.fromRectangle(
  320. rectangle,
  321. minimumHeight,
  322. maximumHeight,
  323. ellipsoid,
  324. orientedBoundingBoxScratch
  325. );
  326. var occluder = new EllipsoidalOccluder(ellipsoid);
  327. var horizonOcclusionPoint = occluder.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid(
  328. boundingSphere.center,
  329. cartesianVertices,
  330. 3,
  331. boundingSphere.center,
  332. minimumHeight,
  333. horizonOcclusionPointScratch
  334. );
  335. var heightRange = maximumHeight - minimumHeight;
  336. var vertices = new Uint16Array(
  337. uBuffer.length + vBuffer.length + heightBuffer.length
  338. );
  339. for (i = 0; i < uBuffer.length; ++i) {
  340. vertices[i] = uBuffer[i];
  341. }
  342. var start = uBuffer.length;
  343. for (i = 0; i < vBuffer.length; ++i) {
  344. vertices[start + i] = vBuffer[i];
  345. }
  346. start += vBuffer.length;
  347. for (i = 0; i < heightBuffer.length; ++i) {
  348. vertices[start + i] =
  349. (maxShort * (heightBuffer[i] - minimumHeight)) / heightRange;
  350. }
  351. var indicesTypedArray = IndexDatatype.createTypedArray(
  352. uBuffer.length,
  353. indices
  354. );
  355. var encodedNormals;
  356. if (hasVertexNormals) {
  357. var normalArray = new Uint8Array(normalBuffer);
  358. transferableObjects.push(
  359. vertices.buffer,
  360. indicesTypedArray.buffer,
  361. normalArray.buffer
  362. );
  363. encodedNormals = normalArray.buffer;
  364. } else {
  365. transferableObjects.push(vertices.buffer, indicesTypedArray.buffer);
  366. }
  367. return {
  368. vertices: vertices.buffer,
  369. encodedNormals: encodedNormals,
  370. indices: indicesTypedArray.buffer,
  371. minimumHeight: minimumHeight,
  372. maximumHeight: maximumHeight,
  373. westIndices: westIndices,
  374. southIndices: southIndices,
  375. eastIndices: eastIndices,
  376. northIndices: northIndices,
  377. boundingSphere: boundingSphere,
  378. orientedBoundingBox: orientedBoundingBox,
  379. horizonOcclusionPoint: horizonOcclusionPoint,
  380. };
  381. }
  382. function Vertex() {
  383. this.vertexBuffer = undefined;
  384. this.index = undefined;
  385. this.first = undefined;
  386. this.second = undefined;
  387. this.ratio = undefined;
  388. }
  389. Vertex.prototype.clone = function (result) {
  390. if (!defined(result)) {
  391. result = new Vertex();
  392. }
  393. result.uBuffer = this.uBuffer;
  394. result.vBuffer = this.vBuffer;
  395. result.heightBuffer = this.heightBuffer;
  396. result.normalBuffer = this.normalBuffer;
  397. result.index = this.index;
  398. result.first = this.first;
  399. result.second = this.second;
  400. result.ratio = this.ratio;
  401. return result;
  402. };
  403. Vertex.prototype.initializeIndexed = function (
  404. uBuffer,
  405. vBuffer,
  406. heightBuffer,
  407. normalBuffer,
  408. index
  409. ) {
  410. this.uBuffer = uBuffer;
  411. this.vBuffer = vBuffer;
  412. this.heightBuffer = heightBuffer;
  413. this.normalBuffer = normalBuffer;
  414. this.index = index;
  415. this.first = undefined;
  416. this.second = undefined;
  417. this.ratio = undefined;
  418. };
  419. Vertex.prototype.initializeFromClipResult = function (
  420. clipResult,
  421. index,
  422. vertices
  423. ) {
  424. var nextIndex = index + 1;
  425. if (clipResult[index] !== -1) {
  426. vertices[clipResult[index]].clone(this);
  427. } else {
  428. this.vertexBuffer = undefined;
  429. this.index = undefined;
  430. this.first = vertices[clipResult[nextIndex]];
  431. ++nextIndex;
  432. this.second = vertices[clipResult[nextIndex]];
  433. ++nextIndex;
  434. this.ratio = clipResult[nextIndex];
  435. ++nextIndex;
  436. }
  437. return nextIndex;
  438. };
  439. Vertex.prototype.getKey = function () {
  440. if (this.isIndexed()) {
  441. return this.index;
  442. }
  443. return JSON.stringify({
  444. first: this.first.getKey(),
  445. second: this.second.getKey(),
  446. ratio: this.ratio,
  447. });
  448. };
  449. Vertex.prototype.isIndexed = function () {
  450. return defined(this.index);
  451. };
  452. Vertex.prototype.getH = function () {
  453. if (defined(this.index)) {
  454. return this.heightBuffer[this.index];
  455. }
  456. return CesiumMath.lerp(this.first.getH(), this.second.getH(), this.ratio);
  457. };
  458. Vertex.prototype.getU = function () {
  459. if (defined(this.index)) {
  460. return this.uBuffer[this.index];
  461. }
  462. return CesiumMath.lerp(this.first.getU(), this.second.getU(), this.ratio);
  463. };
  464. Vertex.prototype.getV = function () {
  465. if (defined(this.index)) {
  466. return this.vBuffer[this.index];
  467. }
  468. return CesiumMath.lerp(this.first.getV(), this.second.getV(), this.ratio);
  469. };
  470. var encodedScratch = new Cartesian2();
  471. // An upsampled triangle may be clipped twice before it is assigned an index
  472. // In this case, we need a buffer to handle the recursion of getNormalX() and getNormalY().
  473. var depth = -1;
  474. var cartesianScratch1 = [new Cartesian3(), new Cartesian3()];
  475. var cartesianScratch2 = [new Cartesian3(), new Cartesian3()];
  476. function lerpOctEncodedNormal(vertex, result) {
  477. ++depth;
  478. var first = cartesianScratch1[depth];
  479. var second = cartesianScratch2[depth];
  480. first = AttributeCompression.octDecode(
  481. vertex.first.getNormalX(),
  482. vertex.first.getNormalY(),
  483. first
  484. );
  485. second = AttributeCompression.octDecode(
  486. vertex.second.getNormalX(),
  487. vertex.second.getNormalY(),
  488. second
  489. );
  490. cartesian3Scratch = Cartesian3.lerp(
  491. first,
  492. second,
  493. vertex.ratio,
  494. cartesian3Scratch
  495. );
  496. Cartesian3.normalize(cartesian3Scratch, cartesian3Scratch);
  497. AttributeCompression.octEncode(cartesian3Scratch, result);
  498. --depth;
  499. return result;
  500. }
  501. Vertex.prototype.getNormalX = function () {
  502. if (defined(this.index)) {
  503. return this.normalBuffer[this.index * 2];
  504. }
  505. encodedScratch = lerpOctEncodedNormal(this, encodedScratch);
  506. return encodedScratch.x;
  507. };
  508. Vertex.prototype.getNormalY = function () {
  509. if (defined(this.index)) {
  510. return this.normalBuffer[this.index * 2 + 1];
  511. }
  512. encodedScratch = lerpOctEncodedNormal(this, encodedScratch);
  513. return encodedScratch.y;
  514. };
  515. var polygonVertices = [];
  516. polygonVertices.push(new Vertex());
  517. polygonVertices.push(new Vertex());
  518. polygonVertices.push(new Vertex());
  519. polygonVertices.push(new Vertex());
  520. function addClippedPolygon(
  521. uBuffer,
  522. vBuffer,
  523. heightBuffer,
  524. normalBuffer,
  525. indices,
  526. vertexMap,
  527. clipped,
  528. triangleVertices,
  529. hasVertexNormals
  530. ) {
  531. if (clipped.length === 0) {
  532. return;
  533. }
  534. var numVertices = 0;
  535. var clippedIndex = 0;
  536. while (clippedIndex < clipped.length) {
  537. clippedIndex = polygonVertices[numVertices++].initializeFromClipResult(
  538. clipped,
  539. clippedIndex,
  540. triangleVertices
  541. );
  542. }
  543. for (var i = 0; i < numVertices; ++i) {
  544. var polygonVertex = polygonVertices[i];
  545. if (!polygonVertex.isIndexed()) {
  546. var key = polygonVertex.getKey();
  547. if (defined(vertexMap[key])) {
  548. polygonVertex.newIndex = vertexMap[key];
  549. } else {
  550. var newIndex = uBuffer.length;
  551. uBuffer.push(polygonVertex.getU());
  552. vBuffer.push(polygonVertex.getV());
  553. heightBuffer.push(polygonVertex.getH());
  554. if (hasVertexNormals) {
  555. normalBuffer.push(polygonVertex.getNormalX());
  556. normalBuffer.push(polygonVertex.getNormalY());
  557. }
  558. polygonVertex.newIndex = newIndex;
  559. vertexMap[key] = newIndex;
  560. }
  561. } else {
  562. polygonVertex.newIndex = vertexMap[polygonVertex.index];
  563. polygonVertex.uBuffer = uBuffer;
  564. polygonVertex.vBuffer = vBuffer;
  565. polygonVertex.heightBuffer = heightBuffer;
  566. if (hasVertexNormals) {
  567. polygonVertex.normalBuffer = normalBuffer;
  568. }
  569. }
  570. }
  571. if (numVertices === 3) {
  572. // A triangle.
  573. indices.push(polygonVertices[0].newIndex);
  574. indices.push(polygonVertices[1].newIndex);
  575. indices.push(polygonVertices[2].newIndex);
  576. } else if (numVertices === 4) {
  577. // A quad - two triangles.
  578. indices.push(polygonVertices[0].newIndex);
  579. indices.push(polygonVertices[1].newIndex);
  580. indices.push(polygonVertices[2].newIndex);
  581. indices.push(polygonVertices[0].newIndex);
  582. indices.push(polygonVertices[2].newIndex);
  583. indices.push(polygonVertices[3].newIndex);
  584. }
  585. }
  586. export default createTaskProcessorWorker(upsampleQuantizedTerrainMesh);