upsampleQuantizedTerrainMesh.js 18 KB

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