upsampleQuantizedTerrainMesh.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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 TerrainEncoding from '../Core/TerrainEncoding.js';
  14. import createTaskProcessorWorker from './createTaskProcessorWorker.js';
  15. var maxShort = 32767;
  16. var halfMaxShort = (maxShort / 2) | 0;
  17. var clipScratch = [];
  18. var clipScratch2 = [];
  19. var verticesScratch = [];
  20. var cartographicScratch = new Cartographic();
  21. var cartesian3Scratch = new Cartesian3();
  22. var uScratch = [];
  23. var vScratch = [];
  24. var heightScratch = [];
  25. var indicesScratch = [];
  26. var normalsScratch = [];
  27. var horizonOcclusionPointScratch = new Cartesian3();
  28. var boundingSphereScratch = new BoundingSphere();
  29. var orientedBoundingBoxScratch = new OrientedBoundingBox();
  30. var decodeTexCoordsScratch = new Cartesian2();
  31. var octEncodedNormalScratch = new Cartesian3();
  32. function upsampleQuantizedTerrainMesh(parameters, transferableObjects) {
  33. var isEastChild = parameters.isEastChild;
  34. var isNorthChild = parameters.isNorthChild;
  35. var minU = isEastChild ? halfMaxShort : 0;
  36. var maxU = isEastChild ? maxShort : halfMaxShort;
  37. var minV = isNorthChild ? halfMaxShort : 0;
  38. var maxV = isNorthChild ? maxShort : halfMaxShort;
  39. var uBuffer = uScratch;
  40. var vBuffer = vScratch;
  41. var heightBuffer = heightScratch;
  42. var normalBuffer = normalsScratch;
  43. uBuffer.length = 0;
  44. vBuffer.length = 0;
  45. heightBuffer.length = 0;
  46. normalBuffer.length = 0;
  47. var indices = indicesScratch;
  48. indices.length = 0;
  49. var vertexMap = {};
  50. var parentVertices = parameters.vertices;
  51. var parentIndices = parameters.indices;
  52. parentIndices = parentIndices.subarray(0, parameters.skirtIndex);
  53. var encoding = TerrainEncoding.clone(parameters.encoding);
  54. var hasVertexNormals = encoding.hasVertexNormals;
  55. var exaggeration = parameters.exaggeration;
  56. var vertexCount = 0;
  57. var quantizedVertexCount = parameters.vertexCountWithoutSkirts;
  58. var parentMinimumHeight = parameters.minimumHeight;
  59. var parentMaximumHeight = parameters.maximumHeight;
  60. var parentUBuffer = new Array(quantizedVertexCount);
  61. var parentVBuffer = new Array(quantizedVertexCount);
  62. var parentHeightBuffer = new Array(quantizedVertexCount);
  63. var parentNormalBuffer = hasVertexNormals ? new Array(quantizedVertexCount * 2) : undefined;
  64. var threshold = 20;
  65. var height;
  66. var i, n;
  67. var u, v;
  68. for (i = 0, n = 0; i < quantizedVertexCount; ++i, n += 2) {
  69. var texCoords = encoding.decodeTextureCoordinates(parentVertices, i, decodeTexCoordsScratch);
  70. height = encoding.decodeHeight(parentVertices, i) / exaggeration;
  71. u = CesiumMath.clamp((texCoords.x * maxShort) | 0, 0, maxShort);
  72. v = CesiumMath.clamp((texCoords.y * maxShort) | 0, 0, maxShort);
  73. parentHeightBuffer[i] = CesiumMath.clamp((((height - parentMinimumHeight) / (parentMaximumHeight - parentMinimumHeight)) * maxShort) | 0, 0, maxShort);
  74. if (u < threshold) {
  75. u = 0;
  76. }
  77. if (v < threshold) {
  78. v = 0;
  79. }
  80. if (maxShort - u < threshold) {
  81. u = maxShort;
  82. }
  83. if (maxShort - v < threshold) {
  84. v = maxShort;
  85. }
  86. parentUBuffer[i] = u;
  87. parentVBuffer[i] = v;
  88. if (hasVertexNormals) {
  89. var encodedNormal = encoding.getOctEncodedNormal(parentVertices, i, octEncodedNormalScratch);
  90. parentNormalBuffer[n] = encodedNormal.x;
  91. parentNormalBuffer[n + 1] = encodedNormal.y;
  92. }
  93. if ((isEastChild && u >= halfMaxShort || !isEastChild && u <= halfMaxShort) &&
  94. (isNorthChild && v >= halfMaxShort || !isNorthChild && v <= halfMaxShort)) {
  95. vertexMap[i] = vertexCount;
  96. uBuffer.push(u);
  97. vBuffer.push(v);
  98. heightBuffer.push(parentHeightBuffer[i]);
  99. if (hasVertexNormals) {
  100. normalBuffer.push(parentNormalBuffer[n]);
  101. normalBuffer.push(parentNormalBuffer[n + 1]);
  102. }
  103. ++vertexCount;
  104. }
  105. }
  106. var triangleVertices = [];
  107. triangleVertices.push(new Vertex());
  108. triangleVertices.push(new Vertex());
  109. triangleVertices.push(new Vertex());
  110. var clippedTriangleVertices = [];
  111. clippedTriangleVertices.push(new Vertex());
  112. clippedTriangleVertices.push(new Vertex());
  113. clippedTriangleVertices.push(new Vertex());
  114. var clippedIndex;
  115. var clipped2;
  116. for (i = 0; i < parentIndices.length; i += 3) {
  117. var i0 = parentIndices[i];
  118. var i1 = parentIndices[i + 1];
  119. var i2 = parentIndices[i + 2];
  120. var u0 = parentUBuffer[i0];
  121. var u1 = parentUBuffer[i1];
  122. var u2 = parentUBuffer[i2];
  123. triangleVertices[0].initializeIndexed(parentUBuffer, parentVBuffer, parentHeightBuffer, parentNormalBuffer, i0);
  124. triangleVertices[1].initializeIndexed(parentUBuffer, parentVBuffer, parentHeightBuffer, parentNormalBuffer, i1);
  125. triangleVertices[2].initializeIndexed(parentUBuffer, parentVBuffer, parentHeightBuffer, parentNormalBuffer, i2);
  126. // Clip triangle on the east-west boundary.
  127. var clipped = Intersections2D.clipTriangleAtAxisAlignedThreshold(halfMaxShort, isEastChild, u0, u1, u2, clipScratch);
  128. // Get the first clipped triangle, if any.
  129. clippedIndex = 0;
  130. if (clippedIndex >= clipped.length) {
  131. continue;
  132. }
  133. clippedIndex = clippedTriangleVertices[0].initializeFromClipResult(clipped, clippedIndex, triangleVertices);
  134. if (clippedIndex >= clipped.length) {
  135. continue;
  136. }
  137. clippedIndex = clippedTriangleVertices[1].initializeFromClipResult(clipped, clippedIndex, triangleVertices);
  138. if (clippedIndex >= clipped.length) {
  139. continue;
  140. }
  141. clippedIndex = clippedTriangleVertices[2].initializeFromClipResult(clipped, clippedIndex, triangleVertices);
  142. // Clip the triangle against the North-south boundary.
  143. clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(halfMaxShort, isNorthChild, clippedTriangleVertices[0].getV(), clippedTriangleVertices[1].getV(), clippedTriangleVertices[2].getV(), clipScratch2);
  144. addClippedPolygon(uBuffer, vBuffer, heightBuffer, normalBuffer, indices, vertexMap, clipped2, clippedTriangleVertices, hasVertexNormals);
  145. // If there's another vertex in the original clipped result,
  146. // it forms a second triangle. Clip it as well.
  147. if (clippedIndex < clipped.length) {
  148. clippedTriangleVertices[2].clone(clippedTriangleVertices[1]);
  149. clippedTriangleVertices[2].initializeFromClipResult(clipped, clippedIndex, triangleVertices);
  150. clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(halfMaxShort, isNorthChild, clippedTriangleVertices[0].getV(), clippedTriangleVertices[1].getV(), clippedTriangleVertices[2].getV(), clipScratch2);
  151. addClippedPolygon(uBuffer, vBuffer, heightBuffer, normalBuffer, indices, vertexMap, clipped2, clippedTriangleVertices, hasVertexNormals);
  152. }
  153. }
  154. var uOffset = isEastChild ? -maxShort : 0;
  155. var vOffset = isNorthChild ? -maxShort : 0;
  156. var westIndices = [];
  157. var southIndices = [];
  158. var eastIndices = [];
  159. var northIndices = [];
  160. var minimumHeight = Number.MAX_VALUE;
  161. var maximumHeight = -minimumHeight;
  162. var cartesianVertices = verticesScratch;
  163. cartesianVertices.length = 0;
  164. var ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
  165. var rectangle = parameters.childRectangle;
  166. var north = rectangle.north;
  167. var south = rectangle.south;
  168. var east = rectangle.east;
  169. var west = rectangle.west;
  170. if (east < west) {
  171. east += CesiumMath.TWO_PI;
  172. }
  173. for (i = 0; i < uBuffer.length; ++i) {
  174. u = Math.round(uBuffer[i]);
  175. if (u <= minU) {
  176. westIndices.push(i);
  177. u = 0;
  178. } else if (u >= maxU) {
  179. eastIndices.push(i);
  180. u = maxShort;
  181. } else {
  182. u = u * 2 + uOffset;
  183. }
  184. uBuffer[i] = u;
  185. v = Math.round(vBuffer[i]);
  186. if (v <= minV) {
  187. southIndices.push(i);
  188. v = 0;
  189. } else if (v >= maxV) {
  190. northIndices.push(i);
  191. v = maxShort;
  192. } else {
  193. v = v * 2 + vOffset;
  194. }
  195. vBuffer[i] = v;
  196. height = CesiumMath.lerp(parentMinimumHeight, parentMaximumHeight, heightBuffer[i] / maxShort);
  197. if (height < minimumHeight) {
  198. minimumHeight = height;
  199. }
  200. if (height > maximumHeight) {
  201. maximumHeight = height;
  202. }
  203. heightBuffer[i] = height;
  204. cartographicScratch.longitude = CesiumMath.lerp(west, east, u / maxShort);
  205. cartographicScratch.latitude = CesiumMath.lerp(south, north, v / maxShort);
  206. cartographicScratch.height = height;
  207. ellipsoid.cartographicToCartesian(cartographicScratch, cartesian3Scratch);
  208. cartesianVertices.push(cartesian3Scratch.x);
  209. cartesianVertices.push(cartesian3Scratch.y);
  210. cartesianVertices.push(cartesian3Scratch.z);
  211. }
  212. var boundingSphere = BoundingSphere.fromVertices(cartesianVertices, Cartesian3.ZERO, 3, boundingSphereScratch);
  213. var orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, ellipsoid, orientedBoundingBoxScratch);
  214. var occluder = new EllipsoidalOccluder(ellipsoid);
  215. var horizonOcclusionPoint = occluder.computeHorizonCullingPointFromVertices(boundingSphere.center, cartesianVertices, 3, boundingSphere.center, horizonOcclusionPointScratch);
  216. var heightRange = maximumHeight - minimumHeight;
  217. var vertices = new Uint16Array(uBuffer.length + vBuffer.length + heightBuffer.length);
  218. for (i = 0; i < uBuffer.length; ++i) {
  219. vertices[i] = uBuffer[i];
  220. }
  221. var start = uBuffer.length;
  222. for (i = 0; i < vBuffer.length; ++i) {
  223. vertices[start + i] = vBuffer[i];
  224. }
  225. start += vBuffer.length;
  226. for (i = 0; i < heightBuffer.length; ++i) {
  227. vertices[start + i] = maxShort * (heightBuffer[i] - minimumHeight) / heightRange;
  228. }
  229. var indicesTypedArray = IndexDatatype.createTypedArray(uBuffer.length, indices);
  230. var encodedNormals;
  231. if (hasVertexNormals) {
  232. var normalArray = new Uint8Array(normalBuffer);
  233. transferableObjects.push(vertices.buffer, indicesTypedArray.buffer, normalArray.buffer);
  234. encodedNormals = normalArray.buffer;
  235. } else {
  236. transferableObjects.push(vertices.buffer, indicesTypedArray.buffer);
  237. }
  238. return {
  239. vertices : vertices.buffer,
  240. encodedNormals : encodedNormals,
  241. indices : indicesTypedArray.buffer,
  242. minimumHeight : minimumHeight,
  243. maximumHeight : maximumHeight,
  244. westIndices : westIndices,
  245. southIndices : southIndices,
  246. eastIndices : eastIndices,
  247. northIndices : northIndices,
  248. boundingSphere : boundingSphere,
  249. orientedBoundingBox : orientedBoundingBox,
  250. horizonOcclusionPoint : horizonOcclusionPoint
  251. };
  252. }
  253. function Vertex() {
  254. this.vertexBuffer = undefined;
  255. this.index = undefined;
  256. this.first = undefined;
  257. this.second = undefined;
  258. this.ratio = undefined;
  259. }
  260. Vertex.prototype.clone = function(result) {
  261. if (!defined(result)) {
  262. result = new Vertex();
  263. }
  264. result.uBuffer = this.uBuffer;
  265. result.vBuffer = this.vBuffer;
  266. result.heightBuffer = this.heightBuffer;
  267. result.normalBuffer = this.normalBuffer;
  268. result.index = this.index;
  269. result.first = this.first;
  270. result.second = this.second;
  271. result.ratio = this.ratio;
  272. return result;
  273. };
  274. Vertex.prototype.initializeIndexed = function(uBuffer, vBuffer, heightBuffer, normalBuffer, index) {
  275. this.uBuffer = uBuffer;
  276. this.vBuffer = vBuffer;
  277. this.heightBuffer = heightBuffer;
  278. this.normalBuffer = normalBuffer;
  279. this.index = index;
  280. this.first = undefined;
  281. this.second = undefined;
  282. this.ratio = undefined;
  283. };
  284. Vertex.prototype.initializeFromClipResult = function(clipResult, index, vertices) {
  285. var nextIndex = index + 1;
  286. if (clipResult[index] !== -1) {
  287. vertices[clipResult[index]].clone(this);
  288. } else {
  289. this.vertexBuffer = undefined;
  290. this.index = undefined;
  291. this.first = vertices[clipResult[nextIndex]];
  292. ++nextIndex;
  293. this.second = vertices[clipResult[nextIndex]];
  294. ++nextIndex;
  295. this.ratio = clipResult[nextIndex];
  296. ++nextIndex;
  297. }
  298. return nextIndex;
  299. };
  300. Vertex.prototype.getKey = function() {
  301. if (this.isIndexed()) {
  302. return this.index;
  303. }
  304. return JSON.stringify({
  305. first : this.first.getKey(),
  306. second : this.second.getKey(),
  307. ratio : this.ratio
  308. });
  309. };
  310. Vertex.prototype.isIndexed = function() {
  311. return defined(this.index);
  312. };
  313. Vertex.prototype.getH = function() {
  314. if (defined(this.index)) {
  315. return this.heightBuffer[this.index];
  316. }
  317. return CesiumMath.lerp(this.first.getH(), this.second.getH(), this.ratio);
  318. };
  319. Vertex.prototype.getU = function() {
  320. if (defined(this.index)) {
  321. return this.uBuffer[this.index];
  322. }
  323. return CesiumMath.lerp(this.first.getU(), this.second.getU(), this.ratio);
  324. };
  325. Vertex.prototype.getV = function() {
  326. if (defined(this.index)) {
  327. return this.vBuffer[this.index];
  328. }
  329. return CesiumMath.lerp(this.first.getV(), this.second.getV(), this.ratio);
  330. };
  331. var encodedScratch = new Cartesian2();
  332. // An upsampled triangle may be clipped twice before it is assigned an index
  333. // In this case, we need a buffer to handle the recursion of getNormalX() and getNormalY().
  334. var depth = -1;
  335. var cartesianScratch1 = [new Cartesian3(), new Cartesian3()];
  336. var cartesianScratch2 = [new Cartesian3(), new Cartesian3()];
  337. function lerpOctEncodedNormal(vertex, result) {
  338. ++depth;
  339. var first = cartesianScratch1[depth];
  340. var second = cartesianScratch2[depth];
  341. first = AttributeCompression.octDecode(vertex.first.getNormalX(), vertex.first.getNormalY(), first);
  342. second = AttributeCompression.octDecode(vertex.second.getNormalX(), vertex.second.getNormalY(), second);
  343. cartesian3Scratch = Cartesian3.lerp(first, second, vertex.ratio, cartesian3Scratch);
  344. Cartesian3.normalize(cartesian3Scratch, cartesian3Scratch);
  345. AttributeCompression.octEncode(cartesian3Scratch, result);
  346. --depth;
  347. return result;
  348. }
  349. Vertex.prototype.getNormalX = function() {
  350. if (defined(this.index)) {
  351. return this.normalBuffer[this.index * 2];
  352. }
  353. encodedScratch = lerpOctEncodedNormal(this, encodedScratch);
  354. return encodedScratch.x;
  355. };
  356. Vertex.prototype.getNormalY = function() {
  357. if (defined(this.index)) {
  358. return this.normalBuffer[this.index * 2 + 1];
  359. }
  360. encodedScratch = lerpOctEncodedNormal(this, encodedScratch);
  361. return encodedScratch.y;
  362. };
  363. var polygonVertices = [];
  364. polygonVertices.push(new Vertex());
  365. polygonVertices.push(new Vertex());
  366. polygonVertices.push(new Vertex());
  367. polygonVertices.push(new Vertex());
  368. function addClippedPolygon(uBuffer, vBuffer, heightBuffer, normalBuffer, indices, vertexMap, clipped, triangleVertices, hasVertexNormals) {
  369. if (clipped.length === 0) {
  370. return;
  371. }
  372. var numVertices = 0;
  373. var clippedIndex = 0;
  374. while (clippedIndex < clipped.length) {
  375. clippedIndex = polygonVertices[numVertices++].initializeFromClipResult(clipped, clippedIndex, triangleVertices);
  376. }
  377. for (var i = 0; i < numVertices; ++i) {
  378. var polygonVertex = polygonVertices[i];
  379. if (!polygonVertex.isIndexed()) {
  380. var key = polygonVertex.getKey();
  381. if (defined(vertexMap[key])) {
  382. polygonVertex.newIndex = vertexMap[key];
  383. } else {
  384. var newIndex = uBuffer.length;
  385. uBuffer.push(polygonVertex.getU());
  386. vBuffer.push(polygonVertex.getV());
  387. heightBuffer.push(polygonVertex.getH());
  388. if (hasVertexNormals) {
  389. normalBuffer.push(polygonVertex.getNormalX());
  390. normalBuffer.push(polygonVertex.getNormalY());
  391. }
  392. polygonVertex.newIndex = newIndex;
  393. vertexMap[key] = newIndex;
  394. }
  395. } else {
  396. polygonVertex.newIndex = vertexMap[polygonVertex.index];
  397. polygonVertex.uBuffer = uBuffer;
  398. polygonVertex.vBuffer = vBuffer;
  399. polygonVertex.heightBuffer = heightBuffer;
  400. if (hasVertexNormals) {
  401. polygonVertex.normalBuffer = normalBuffer;
  402. }
  403. }
  404. }
  405. if (numVertices === 3) {
  406. // A triangle.
  407. indices.push(polygonVertices[0].newIndex);
  408. indices.push(polygonVertices[1].newIndex);
  409. indices.push(polygonVertices[2].newIndex);
  410. } else if (numVertices === 4) {
  411. // A quad - two triangles.
  412. indices.push(polygonVertices[0].newIndex);
  413. indices.push(polygonVertices[1].newIndex);
  414. indices.push(polygonVertices[2].newIndex);
  415. indices.push(polygonVertices[0].newIndex);
  416. indices.push(polygonVertices[2].newIndex);
  417. indices.push(polygonVertices[3].newIndex);
  418. }
  419. }
  420. export default createTaskProcessorWorker(upsampleQuantizedTerrainMesh);