智慧水务管理系统 - 精河县供水工程综合管理平台

TerrainFillMesh.js 56KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222
  1. import AttributeCompression from "../Core/AttributeCompression.js";
  2. import binarySearch from "../Core/binarySearch.js";
  3. import BoundingSphere from "../Core/BoundingSphere.js";
  4. import Cartesian2 from "../Core/Cartesian2.js";
  5. import Cartesian3 from "../Core/Cartesian3.js";
  6. import Cartesian4 from "../Core/Cartesian4.js";
  7. import Cartographic from "../Core/Cartographic.js";
  8. import defined from "../Core/defined.js";
  9. import DeveloperError from "../Core/DeveloperError.js";
  10. import HeightmapTerrainData from "../Core/HeightmapTerrainData.js";
  11. import CesiumMath from "../Core/Math.js";
  12. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  13. import Queue from "../Core/Queue.js";
  14. import Rectangle from "../Core/Rectangle.js";
  15. import TerrainEncoding from "../Core/TerrainEncoding.js";
  16. import TerrainMesh from "../Core/TerrainMesh.js";
  17. import TileEdge from "../Core/TileEdge.js";
  18. import WebMercatorProjection from "../Core/WebMercatorProjection.js";
  19. import GlobeSurfaceTile from "./GlobeSurfaceTile.js";
  20. import TileSelectionResult from "./TileSelectionResult.js";
  21. function TerrainFillMesh(tile) {
  22. this.tile = tile;
  23. this.frameLastUpdated = undefined;
  24. this.westMeshes = []; // north to south (CCW)
  25. this.westTiles = [];
  26. this.southMeshes = []; // west to east (CCW)
  27. this.southTiles = [];
  28. this.eastMeshes = []; // south to north (CCW)
  29. this.eastTiles = [];
  30. this.northMeshes = []; // east to west (CCW)
  31. this.northTiles = [];
  32. this.southwestMesh = undefined;
  33. this.southwestTile = undefined;
  34. this.southeastMesh = undefined;
  35. this.southeastTile = undefined;
  36. this.northwestMesh = undefined;
  37. this.northwestTile = undefined;
  38. this.northeastMesh = undefined;
  39. this.northeastTile = undefined;
  40. this.changedThisFrame = true;
  41. this.visitedFrame = undefined;
  42. this.enqueuedFrame = undefined;
  43. this.mesh = undefined;
  44. this.vertexArray = undefined;
  45. this.waterMaskTexture = undefined;
  46. this.waterMaskTranslationAndScale = new Cartesian4();
  47. }
  48. TerrainFillMesh.prototype.update = function (
  49. tileProvider,
  50. frameState,
  51. vertexArraysToDestroy,
  52. ) {
  53. if (this.changedThisFrame) {
  54. createFillMesh(tileProvider, frameState, this.tile, vertexArraysToDestroy);
  55. this.changedThisFrame = false;
  56. }
  57. };
  58. TerrainFillMesh.prototype.destroy = function (vertexArraysToDestroy) {
  59. this._destroyVertexArray(vertexArraysToDestroy);
  60. if (defined(this.waterMaskTexture)) {
  61. --this.waterMaskTexture.referenceCount;
  62. if (this.waterMaskTexture.referenceCount === 0) {
  63. this.waterMaskTexture.destroy();
  64. }
  65. this.waterMaskTexture = undefined;
  66. }
  67. return undefined;
  68. };
  69. TerrainFillMesh.prototype._destroyVertexArray = function (
  70. vertexArraysToDestroy,
  71. ) {
  72. if (defined(this.vertexArray)) {
  73. if (defined(vertexArraysToDestroy)) {
  74. vertexArraysToDestroy.push(this.vertexArray);
  75. } else {
  76. GlobeSurfaceTile._freeVertexArray(this.vertexArray);
  77. }
  78. this.vertexArray = undefined;
  79. }
  80. };
  81. const traversalQueueScratch = new Queue();
  82. TerrainFillMesh.updateFillTiles = function (
  83. tileProvider,
  84. renderedTiles,
  85. frameState,
  86. vertexArraysToDestroy,
  87. ) {
  88. // We want our fill tiles to look natural, which means they should align perfectly with
  89. // adjacent loaded tiles, and their edges that are not adjacent to loaded tiles should have
  90. // sensible heights (e.g. the average of the heights of loaded edges). Some fill tiles may
  91. // be adjacent only to other fill tiles, and in that case heights should be assigned fanning
  92. // outward from the loaded tiles so that there are no sudden changes in height.
  93. // We do this with a breadth-first traversal of the rendered tiles, starting with the loaded
  94. // ones. Graph nodes are tiles and graph edges connect to other rendered tiles that are spatially adjacent
  95. // to those tiles. As we visit each node, we propagate tile edges to adjacent tiles. If there's no data
  96. // for a tile edge, we create an edge with an average height and then propagate it. If an edge is partially defined
  97. // (e.g. an edge is adjacent to multiple more-detailed tiles and only some of them are loaded), we
  98. // fill in the rest of the edge with the same height.
  99. const quadtree = tileProvider._quadtree;
  100. const levelZeroTiles = quadtree._levelZeroTiles;
  101. const lastSelectionFrameNumber = quadtree._lastSelectionFrameNumber;
  102. const traversalQueue = traversalQueueScratch;
  103. traversalQueue.clear();
  104. // Add the tiles with real geometry to the traversal queue.
  105. for (let i = 0; i < renderedTiles.length; ++i) {
  106. const renderedTile = renderedTiles[i];
  107. if (defined(renderedTile.data.vertexArray)) {
  108. traversalQueue.enqueue(renderedTiles[i]);
  109. }
  110. }
  111. let tile = traversalQueue.dequeue();
  112. while (tile !== undefined) {
  113. const tileToWest = tile.findTileToWest(levelZeroTiles);
  114. const tileToSouth = tile.findTileToSouth(levelZeroTiles);
  115. const tileToEast = tile.findTileToEast(levelZeroTiles);
  116. const tileToNorth = tile.findTileToNorth(levelZeroTiles);
  117. visitRenderedTiles(
  118. tileProvider,
  119. frameState,
  120. tile,
  121. tileToWest,
  122. lastSelectionFrameNumber,
  123. TileEdge.EAST,
  124. false,
  125. traversalQueue,
  126. vertexArraysToDestroy,
  127. );
  128. visitRenderedTiles(
  129. tileProvider,
  130. frameState,
  131. tile,
  132. tileToSouth,
  133. lastSelectionFrameNumber,
  134. TileEdge.NORTH,
  135. false,
  136. traversalQueue,
  137. vertexArraysToDestroy,
  138. );
  139. visitRenderedTiles(
  140. tileProvider,
  141. frameState,
  142. tile,
  143. tileToEast,
  144. lastSelectionFrameNumber,
  145. TileEdge.WEST,
  146. false,
  147. traversalQueue,
  148. vertexArraysToDestroy,
  149. );
  150. visitRenderedTiles(
  151. tileProvider,
  152. frameState,
  153. tile,
  154. tileToNorth,
  155. lastSelectionFrameNumber,
  156. TileEdge.SOUTH,
  157. false,
  158. traversalQueue,
  159. vertexArraysToDestroy,
  160. );
  161. const tileToNorthwest = tileToWest.findTileToNorth(levelZeroTiles);
  162. const tileToSouthwest = tileToWest.findTileToSouth(levelZeroTiles);
  163. const tileToNortheast = tileToEast.findTileToNorth(levelZeroTiles);
  164. const tileToSoutheast = tileToEast.findTileToSouth(levelZeroTiles);
  165. visitRenderedTiles(
  166. tileProvider,
  167. frameState,
  168. tile,
  169. tileToNorthwest,
  170. lastSelectionFrameNumber,
  171. TileEdge.SOUTHEAST,
  172. false,
  173. traversalQueue,
  174. vertexArraysToDestroy,
  175. );
  176. visitRenderedTiles(
  177. tileProvider,
  178. frameState,
  179. tile,
  180. tileToNortheast,
  181. lastSelectionFrameNumber,
  182. TileEdge.SOUTHWEST,
  183. false,
  184. traversalQueue,
  185. vertexArraysToDestroy,
  186. );
  187. visitRenderedTiles(
  188. tileProvider,
  189. frameState,
  190. tile,
  191. tileToSouthwest,
  192. lastSelectionFrameNumber,
  193. TileEdge.NORTHEAST,
  194. false,
  195. traversalQueue,
  196. vertexArraysToDestroy,
  197. );
  198. visitRenderedTiles(
  199. tileProvider,
  200. frameState,
  201. tile,
  202. tileToSoutheast,
  203. lastSelectionFrameNumber,
  204. TileEdge.NORTHWEST,
  205. false,
  206. traversalQueue,
  207. vertexArraysToDestroy,
  208. );
  209. tile = traversalQueue.dequeue();
  210. }
  211. };
  212. function visitRenderedTiles(
  213. tileProvider,
  214. frameState,
  215. sourceTile,
  216. startTile,
  217. currentFrameNumber,
  218. tileEdge,
  219. downOnly,
  220. traversalQueue,
  221. vertexArraysToDestroy,
  222. ) {
  223. if (startTile === undefined) {
  224. // There are no tiles North or South of the poles.
  225. return;
  226. }
  227. let tile = startTile;
  228. while (
  229. tile &&
  230. (tile._lastSelectionResultFrame !== currentFrameNumber ||
  231. TileSelectionResult.wasKicked(tile._lastSelectionResult) ||
  232. TileSelectionResult.originalResult(tile._lastSelectionResult) ===
  233. TileSelectionResult.CULLED)
  234. ) {
  235. // This tile wasn't visited or it was visited and then kicked, so walk up to find the closest ancestor that was rendered.
  236. // We also walk up if the tile was culled, because if siblings were kicked an ancestor may have been rendered.
  237. if (downOnly) {
  238. return;
  239. }
  240. const parent = tile.parent;
  241. if (tileEdge >= TileEdge.NORTHWEST && parent !== undefined) {
  242. // When we're looking for a corner, verify that the parent tile is still relevant.
  243. // That is, the parent and child must share the corner in question.
  244. switch (tileEdge) {
  245. case TileEdge.NORTHWEST:
  246. tile = tile === parent.northwestChild ? parent : undefined;
  247. break;
  248. case TileEdge.NORTHEAST:
  249. tile = tile === parent.northeastChild ? parent : undefined;
  250. break;
  251. case TileEdge.SOUTHWEST:
  252. tile = tile === parent.southwestChild ? parent : undefined;
  253. break;
  254. case TileEdge.SOUTHEAST:
  255. tile = tile === parent.southeastChild ? parent : undefined;
  256. break;
  257. }
  258. } else {
  259. tile = parent;
  260. }
  261. }
  262. if (tile === undefined) {
  263. return;
  264. }
  265. if (tile._lastSelectionResult === TileSelectionResult.RENDERED) {
  266. if (defined(tile.data.vertexArray)) {
  267. // No further processing necessary for renderable tiles.
  268. return;
  269. }
  270. visitTile(
  271. tileProvider,
  272. frameState,
  273. sourceTile,
  274. tile,
  275. tileEdge,
  276. currentFrameNumber,
  277. traversalQueue,
  278. vertexArraysToDestroy,
  279. );
  280. return;
  281. }
  282. if (
  283. TileSelectionResult.originalResult(startTile._lastSelectionResult) ===
  284. TileSelectionResult.CULLED
  285. ) {
  286. return;
  287. }
  288. // This tile was refined, so find rendered children, if any.
  289. // Visit the tiles in counter-clockwise order.
  290. switch (tileEdge) {
  291. case TileEdge.WEST:
  292. visitRenderedTiles(
  293. tileProvider,
  294. frameState,
  295. sourceTile,
  296. startTile.northwestChild,
  297. currentFrameNumber,
  298. tileEdge,
  299. true,
  300. traversalQueue,
  301. vertexArraysToDestroy,
  302. );
  303. visitRenderedTiles(
  304. tileProvider,
  305. frameState,
  306. sourceTile,
  307. startTile.southwestChild,
  308. currentFrameNumber,
  309. tileEdge,
  310. true,
  311. traversalQueue,
  312. vertexArraysToDestroy,
  313. );
  314. break;
  315. case TileEdge.EAST:
  316. visitRenderedTiles(
  317. tileProvider,
  318. frameState,
  319. sourceTile,
  320. startTile.southeastChild,
  321. currentFrameNumber,
  322. tileEdge,
  323. true,
  324. traversalQueue,
  325. vertexArraysToDestroy,
  326. );
  327. visitRenderedTiles(
  328. tileProvider,
  329. frameState,
  330. sourceTile,
  331. startTile.northeastChild,
  332. currentFrameNumber,
  333. tileEdge,
  334. true,
  335. traversalQueue,
  336. vertexArraysToDestroy,
  337. );
  338. break;
  339. case TileEdge.SOUTH:
  340. visitRenderedTiles(
  341. tileProvider,
  342. frameState,
  343. sourceTile,
  344. startTile.southwestChild,
  345. currentFrameNumber,
  346. tileEdge,
  347. true,
  348. traversalQueue,
  349. vertexArraysToDestroy,
  350. );
  351. visitRenderedTiles(
  352. tileProvider,
  353. frameState,
  354. sourceTile,
  355. startTile.southeastChild,
  356. currentFrameNumber,
  357. tileEdge,
  358. true,
  359. traversalQueue,
  360. vertexArraysToDestroy,
  361. );
  362. break;
  363. case TileEdge.NORTH:
  364. visitRenderedTiles(
  365. tileProvider,
  366. frameState,
  367. sourceTile,
  368. startTile.northeastChild,
  369. currentFrameNumber,
  370. tileEdge,
  371. true,
  372. traversalQueue,
  373. vertexArraysToDestroy,
  374. );
  375. visitRenderedTiles(
  376. tileProvider,
  377. frameState,
  378. sourceTile,
  379. startTile.northwestChild,
  380. currentFrameNumber,
  381. tileEdge,
  382. true,
  383. traversalQueue,
  384. vertexArraysToDestroy,
  385. );
  386. break;
  387. case TileEdge.NORTHWEST:
  388. visitRenderedTiles(
  389. tileProvider,
  390. frameState,
  391. sourceTile,
  392. startTile.northwestChild,
  393. currentFrameNumber,
  394. tileEdge,
  395. true,
  396. traversalQueue,
  397. vertexArraysToDestroy,
  398. );
  399. break;
  400. case TileEdge.NORTHEAST:
  401. visitRenderedTiles(
  402. tileProvider,
  403. frameState,
  404. sourceTile,
  405. startTile.northeastChild,
  406. currentFrameNumber,
  407. tileEdge,
  408. true,
  409. traversalQueue,
  410. vertexArraysToDestroy,
  411. );
  412. break;
  413. case TileEdge.SOUTHWEST:
  414. visitRenderedTiles(
  415. tileProvider,
  416. frameState,
  417. sourceTile,
  418. startTile.southwestChild,
  419. currentFrameNumber,
  420. tileEdge,
  421. true,
  422. traversalQueue,
  423. vertexArraysToDestroy,
  424. );
  425. break;
  426. case TileEdge.SOUTHEAST:
  427. visitRenderedTiles(
  428. tileProvider,
  429. frameState,
  430. sourceTile,
  431. startTile.southeastChild,
  432. currentFrameNumber,
  433. tileEdge,
  434. true,
  435. traversalQueue,
  436. vertexArraysToDestroy,
  437. );
  438. break;
  439. default:
  440. throw new DeveloperError("Invalid edge");
  441. }
  442. }
  443. function visitTile(
  444. tileProvider,
  445. frameState,
  446. sourceTile,
  447. destinationTile,
  448. tileEdge,
  449. frameNumber,
  450. traversalQueue,
  451. vertexArraysToDestroy,
  452. ) {
  453. const destinationSurfaceTile = destinationTile.data;
  454. if (destinationSurfaceTile.fill === undefined) {
  455. destinationSurfaceTile.fill = new TerrainFillMesh(destinationTile);
  456. } else if (destinationSurfaceTile.fill.visitedFrame === frameNumber) {
  457. // Don't propagate edges to tiles that have already been visited this frame.
  458. return;
  459. }
  460. if (destinationSurfaceTile.fill.enqueuedFrame !== frameNumber) {
  461. // First time visiting this tile this frame, add it to the traversal queue.
  462. destinationSurfaceTile.fill.enqueuedFrame = frameNumber;
  463. destinationSurfaceTile.fill.changedThisFrame = false;
  464. traversalQueue.enqueue(destinationTile);
  465. }
  466. propagateEdge(
  467. tileProvider,
  468. frameState,
  469. sourceTile,
  470. destinationTile,
  471. tileEdge,
  472. vertexArraysToDestroy,
  473. );
  474. }
  475. function propagateEdge(
  476. tileProvider,
  477. frameState,
  478. sourceTile,
  479. destinationTile,
  480. tileEdge,
  481. vertexArraysToDestroy,
  482. ) {
  483. const destinationFill = destinationTile.data.fill;
  484. let sourceMesh;
  485. const sourceFill = sourceTile.data.fill;
  486. if (defined(sourceFill)) {
  487. sourceFill.visitedFrame = frameState.frameNumber;
  488. // Source is a fill, create/update it if necessary.
  489. if (sourceFill.changedThisFrame) {
  490. createFillMesh(
  491. tileProvider,
  492. frameState,
  493. sourceTile,
  494. vertexArraysToDestroy,
  495. );
  496. sourceFill.changedThisFrame = false;
  497. }
  498. sourceMesh = sourceTile.data.fill.mesh;
  499. } else {
  500. sourceMesh = sourceTile.data.mesh;
  501. }
  502. let edgeMeshes;
  503. let edgeTiles;
  504. switch (tileEdge) {
  505. case TileEdge.WEST:
  506. edgeMeshes = destinationFill.westMeshes;
  507. edgeTiles = destinationFill.westTiles;
  508. break;
  509. case TileEdge.SOUTH:
  510. edgeMeshes = destinationFill.southMeshes;
  511. edgeTiles = destinationFill.southTiles;
  512. break;
  513. case TileEdge.EAST:
  514. edgeMeshes = destinationFill.eastMeshes;
  515. edgeTiles = destinationFill.eastTiles;
  516. break;
  517. case TileEdge.NORTH:
  518. edgeMeshes = destinationFill.northMeshes;
  519. edgeTiles = destinationFill.northTiles;
  520. break;
  521. // Corners are simpler.
  522. case TileEdge.NORTHWEST:
  523. destinationFill.changedThisFrame =
  524. destinationFill.changedThisFrame ||
  525. destinationFill.northwestMesh !== sourceMesh;
  526. destinationFill.northwestMesh = sourceMesh;
  527. destinationFill.northwestTile = sourceTile;
  528. return;
  529. case TileEdge.NORTHEAST:
  530. destinationFill.changedThisFrame =
  531. destinationFill.changedThisFrame ||
  532. destinationFill.northeastMesh !== sourceMesh;
  533. destinationFill.northeastMesh = sourceMesh;
  534. destinationFill.northeastTile = sourceTile;
  535. return;
  536. case TileEdge.SOUTHWEST:
  537. destinationFill.changedThisFrame =
  538. destinationFill.changedThisFrame ||
  539. destinationFill.southwestMesh !== sourceMesh;
  540. destinationFill.southwestMesh = sourceMesh;
  541. destinationFill.southwestTile = sourceTile;
  542. return;
  543. case TileEdge.SOUTHEAST:
  544. destinationFill.changedThisFrame =
  545. destinationFill.changedThisFrame ||
  546. destinationFill.southeastMesh !== sourceMesh;
  547. destinationFill.southeastMesh = sourceMesh;
  548. destinationFill.southeastTile = sourceTile;
  549. return;
  550. }
  551. if (sourceTile.level <= destinationTile.level) {
  552. // Source edge completely spans the destination edge.
  553. destinationFill.changedThisFrame =
  554. destinationFill.changedThisFrame ||
  555. edgeMeshes[0] !== sourceMesh ||
  556. edgeMeshes.length !== 1;
  557. edgeMeshes[0] = sourceMesh;
  558. edgeTiles[0] = sourceTile;
  559. edgeMeshes.length = 1;
  560. edgeTiles.length = 1;
  561. return;
  562. }
  563. // Source edge is a subset of the destination edge.
  564. // Figure out the range of meshes we're replacing.
  565. let startIndex, endIndex, existingTile, existingRectangle;
  566. const sourceRectangle = sourceTile.rectangle;
  567. let epsilon;
  568. const destinationRectangle = destinationTile.rectangle;
  569. switch (tileEdge) {
  570. case TileEdge.WEST:
  571. epsilon =
  572. (destinationRectangle.north - destinationRectangle.south) *
  573. CesiumMath.EPSILON5;
  574. for (startIndex = 0; startIndex < edgeTiles.length; ++startIndex) {
  575. existingTile = edgeTiles[startIndex];
  576. existingRectangle = existingTile.rectangle;
  577. if (
  578. CesiumMath.greaterThan(
  579. sourceRectangle.north,
  580. existingRectangle.south,
  581. epsilon,
  582. )
  583. ) {
  584. break;
  585. }
  586. }
  587. for (endIndex = startIndex; endIndex < edgeTiles.length; ++endIndex) {
  588. existingTile = edgeTiles[endIndex];
  589. existingRectangle = existingTile.rectangle;
  590. if (
  591. CesiumMath.greaterThanOrEquals(
  592. sourceRectangle.south,
  593. existingRectangle.north,
  594. epsilon,
  595. )
  596. ) {
  597. break;
  598. }
  599. }
  600. break;
  601. case TileEdge.SOUTH:
  602. epsilon =
  603. (destinationRectangle.east - destinationRectangle.west) *
  604. CesiumMath.EPSILON5;
  605. for (startIndex = 0; startIndex < edgeTiles.length; ++startIndex) {
  606. existingTile = edgeTiles[startIndex];
  607. existingRectangle = existingTile.rectangle;
  608. if (
  609. CesiumMath.lessThan(
  610. sourceRectangle.west,
  611. existingRectangle.east,
  612. epsilon,
  613. )
  614. ) {
  615. break;
  616. }
  617. }
  618. for (endIndex = startIndex; endIndex < edgeTiles.length; ++endIndex) {
  619. existingTile = edgeTiles[endIndex];
  620. existingRectangle = existingTile.rectangle;
  621. if (
  622. CesiumMath.lessThanOrEquals(
  623. sourceRectangle.east,
  624. existingRectangle.west,
  625. epsilon,
  626. )
  627. ) {
  628. break;
  629. }
  630. }
  631. break;
  632. case TileEdge.EAST:
  633. epsilon =
  634. (destinationRectangle.north - destinationRectangle.south) *
  635. CesiumMath.EPSILON5;
  636. for (startIndex = 0; startIndex < edgeTiles.length; ++startIndex) {
  637. existingTile = edgeTiles[startIndex];
  638. existingRectangle = existingTile.rectangle;
  639. if (
  640. CesiumMath.lessThan(
  641. sourceRectangle.south,
  642. existingRectangle.north,
  643. epsilon,
  644. )
  645. ) {
  646. break;
  647. }
  648. }
  649. for (endIndex = startIndex; endIndex < edgeTiles.length; ++endIndex) {
  650. existingTile = edgeTiles[endIndex];
  651. existingRectangle = existingTile.rectangle;
  652. if (
  653. CesiumMath.lessThanOrEquals(
  654. sourceRectangle.north,
  655. existingRectangle.south,
  656. epsilon,
  657. )
  658. ) {
  659. break;
  660. }
  661. }
  662. break;
  663. case TileEdge.NORTH:
  664. epsilon =
  665. (destinationRectangle.east - destinationRectangle.west) *
  666. CesiumMath.EPSILON5;
  667. for (startIndex = 0; startIndex < edgeTiles.length; ++startIndex) {
  668. existingTile = edgeTiles[startIndex];
  669. existingRectangle = existingTile.rectangle;
  670. if (
  671. CesiumMath.greaterThan(
  672. sourceRectangle.east,
  673. existingRectangle.west,
  674. epsilon,
  675. )
  676. ) {
  677. break;
  678. }
  679. }
  680. for (endIndex = startIndex; endIndex < edgeTiles.length; ++endIndex) {
  681. existingTile = edgeTiles[endIndex];
  682. existingRectangle = existingTile.rectangle;
  683. if (
  684. CesiumMath.greaterThanOrEquals(
  685. sourceRectangle.west,
  686. existingRectangle.east,
  687. epsilon,
  688. )
  689. ) {
  690. break;
  691. }
  692. }
  693. break;
  694. }
  695. if (endIndex - startIndex === 1) {
  696. destinationFill.changedThisFrame =
  697. destinationFill.changedThisFrame || edgeMeshes[startIndex] !== sourceMesh;
  698. edgeMeshes[startIndex] = sourceMesh;
  699. edgeTiles[startIndex] = sourceTile;
  700. } else {
  701. destinationFill.changedThisFrame = true;
  702. edgeMeshes.splice(startIndex, endIndex - startIndex, sourceMesh);
  703. edgeTiles.splice(startIndex, endIndex - startIndex, sourceTile);
  704. }
  705. }
  706. const cartographicScratch = new Cartographic();
  707. const centerCartographicScratch = new Cartographic();
  708. const cartesianScratch = new Cartesian3();
  709. const normalScratch = new Cartesian3();
  710. const octEncodedNormalScratch = new Cartesian2();
  711. const uvScratch2 = new Cartesian2();
  712. const uvScratch = new Cartesian2();
  713. function HeightAndNormal() {
  714. this.height = 0.0;
  715. this.encodedNormal = new Cartesian2();
  716. }
  717. function fillMissingCorner(
  718. fill,
  719. ellipsoid,
  720. u,
  721. v,
  722. corner,
  723. adjacentCorner1,
  724. adjacentCorner2,
  725. oppositeCorner,
  726. vertex,
  727. ) {
  728. if (defined(corner)) {
  729. return corner;
  730. }
  731. let height;
  732. if (defined(adjacentCorner1) && defined(adjacentCorner2)) {
  733. height = (adjacentCorner1.height + adjacentCorner2.height) * 0.5;
  734. } else if (defined(adjacentCorner1)) {
  735. height = adjacentCorner1.height;
  736. } else if (defined(adjacentCorner2)) {
  737. height = adjacentCorner2.height;
  738. } else if (defined(oppositeCorner)) {
  739. height = oppositeCorner.height;
  740. } else {
  741. const surfaceTile = fill.tile.data;
  742. const tileBoundingRegion = surfaceTile.tileBoundingRegion;
  743. let minimumHeight = 0.0;
  744. let maximumHeight = 0.0;
  745. if (defined(tileBoundingRegion)) {
  746. minimumHeight = tileBoundingRegion.minimumHeight;
  747. maximumHeight = tileBoundingRegion.maximumHeight;
  748. }
  749. height = (minimumHeight + maximumHeight) * 0.5;
  750. }
  751. getVertexWithHeightAtCorner(fill, ellipsoid, u, v, height, vertex);
  752. return vertex;
  753. }
  754. const heightRangeScratch = {
  755. minimumHeight: 0.0,
  756. maximumHeight: 0.0,
  757. };
  758. const scratchCenter = new Cartesian3();
  759. const swVertexScratch = new HeightAndNormal();
  760. const seVertexScratch = new HeightAndNormal();
  761. const nwVertexScratch = new HeightAndNormal();
  762. const neVertexScratch = new HeightAndNormal();
  763. const heightmapBuffer =
  764. typeof Uint8Array !== "undefined" ? new Uint8Array(9 * 9) : undefined;
  765. const scratchCreateMeshSyncOptions = {
  766. tilingScheme: undefined,
  767. x: 0,
  768. y: 0,
  769. level: 0,
  770. exaggeration: 1.0,
  771. exaggerationRelativeHeight: 0.0,
  772. };
  773. function createFillMesh(tileProvider, frameState, tile, vertexArraysToDestroy) {
  774. GlobeSurfaceTile.initialize(
  775. tile,
  776. tileProvider.terrainProvider,
  777. tileProvider._imageryLayers,
  778. );
  779. const surfaceTile = tile.data;
  780. const fill = surfaceTile.fill;
  781. const rectangle = tile.rectangle;
  782. const exaggeration = frameState.verticalExaggeration;
  783. const exaggerationRelativeHeight =
  784. frameState.verticalExaggerationRelativeHeight;
  785. const hasExaggeration = exaggeration !== 1.0;
  786. const ellipsoid = tile.tilingScheme.ellipsoid;
  787. let nwCorner = getCorner(
  788. fill,
  789. ellipsoid,
  790. 0.0,
  791. 1.0,
  792. fill.northwestTile,
  793. fill.northwestMesh,
  794. fill.northTiles,
  795. fill.northMeshes,
  796. fill.westTiles,
  797. fill.westMeshes,
  798. nwVertexScratch,
  799. );
  800. let swCorner = getCorner(
  801. fill,
  802. ellipsoid,
  803. 0.0,
  804. 0.0,
  805. fill.southwestTile,
  806. fill.southwestMesh,
  807. fill.westTiles,
  808. fill.westMeshes,
  809. fill.southTiles,
  810. fill.southMeshes,
  811. swVertexScratch,
  812. );
  813. let seCorner = getCorner(
  814. fill,
  815. ellipsoid,
  816. 1.0,
  817. 0.0,
  818. fill.southeastTile,
  819. fill.southeastMesh,
  820. fill.southTiles,
  821. fill.southMeshes,
  822. fill.eastTiles,
  823. fill.eastMeshes,
  824. seVertexScratch,
  825. );
  826. let neCorner = getCorner(
  827. fill,
  828. ellipsoid,
  829. 1.0,
  830. 1.0,
  831. fill.northeastTile,
  832. fill.northeastMesh,
  833. fill.eastTiles,
  834. fill.eastMeshes,
  835. fill.northTiles,
  836. fill.northMeshes,
  837. neVertexScratch,
  838. );
  839. nwCorner = fillMissingCorner(
  840. fill,
  841. ellipsoid,
  842. 0.0,
  843. 1.0,
  844. nwCorner,
  845. swCorner,
  846. neCorner,
  847. seCorner,
  848. nwVertexScratch,
  849. );
  850. swCorner = fillMissingCorner(
  851. fill,
  852. ellipsoid,
  853. 0.0,
  854. 0.0,
  855. swCorner,
  856. nwCorner,
  857. seCorner,
  858. neCorner,
  859. swVertexScratch,
  860. );
  861. seCorner = fillMissingCorner(
  862. fill,
  863. ellipsoid,
  864. 1.0,
  865. 1.0,
  866. seCorner,
  867. swCorner,
  868. neCorner,
  869. nwCorner,
  870. seVertexScratch,
  871. );
  872. neCorner = fillMissingCorner(
  873. fill,
  874. ellipsoid,
  875. 1.0,
  876. 1.0,
  877. neCorner,
  878. seCorner,
  879. nwCorner,
  880. swCorner,
  881. neVertexScratch,
  882. );
  883. const southwestHeight = swCorner.height;
  884. const southeastHeight = seCorner.height;
  885. const northwestHeight = nwCorner.height;
  886. const northeastHeight = neCorner.height;
  887. let minimumHeight = Math.min(
  888. southwestHeight,
  889. southeastHeight,
  890. northwestHeight,
  891. northeastHeight,
  892. );
  893. let maximumHeight = Math.max(
  894. southwestHeight,
  895. southeastHeight,
  896. northwestHeight,
  897. northeastHeight,
  898. );
  899. const middleHeight = (minimumHeight + maximumHeight) * 0.5;
  900. let i;
  901. let len;
  902. // For low-detail tiles, our usual fill tile approach will create tiles that
  903. // look really blocky because they don't have enough vertices to account for the
  904. // Earth's curvature. But the height range will also typically be well within
  905. // the allowed geometric error for those levels. So fill such tiles with a
  906. // constant-height heightmap.
  907. const geometricError = tileProvider.getLevelMaximumGeometricError(tile.level);
  908. const minCutThroughRadius = ellipsoid.maximumRadius - geometricError;
  909. let maxTileWidth =
  910. Math.acos(minCutThroughRadius / ellipsoid.maximumRadius) * 4.0;
  911. // When the tile width is greater than maxTileWidth as computed above, the error
  912. // of a normal fill tile from globe curvature alone will exceed the allowed geometric
  913. // error. Terrain won't change that much. However, we can allow more error than that.
  914. // A little blockiness during load is acceptable. For the WGS84 ellipsoid and
  915. // standard geometric error setup, the value here will have us use a heightmap
  916. // at levels 1, 2, and 3.
  917. maxTileWidth *= 1.5;
  918. if (
  919. rectangle.width > maxTileWidth &&
  920. maximumHeight - minimumHeight <= geometricError
  921. ) {
  922. const terrainData = new HeightmapTerrainData({
  923. width: 9,
  924. height: 9,
  925. buffer: heightmapBuffer,
  926. structure: {
  927. // Use the maximum as the constant height so that this tile's skirt
  928. // covers any cracks with adjacent tiles.
  929. heightOffset: maximumHeight,
  930. },
  931. });
  932. const createMeshSyncOptions = scratchCreateMeshSyncOptions;
  933. createMeshSyncOptions.tilingScheme = tile.tilingScheme;
  934. createMeshSyncOptions.x = tile.x;
  935. createMeshSyncOptions.y = tile.y;
  936. createMeshSyncOptions.level = tile.level;
  937. createMeshSyncOptions.exaggeration = exaggeration;
  938. createMeshSyncOptions.exaggerationRelativeHeight =
  939. exaggerationRelativeHeight;
  940. fill.mesh = terrainData._createMeshSync(createMeshSyncOptions);
  941. } else {
  942. const hasGeodeticSurfaceNormals = hasExaggeration;
  943. const centerCartographic = Rectangle.center(
  944. rectangle,
  945. centerCartographicScratch,
  946. );
  947. centerCartographic.height = middleHeight;
  948. const center = ellipsoid.cartographicToCartesian(
  949. centerCartographic,
  950. scratchCenter,
  951. );
  952. const encoding = new TerrainEncoding(
  953. center,
  954. undefined,
  955. undefined,
  956. undefined,
  957. undefined,
  958. true,
  959. true,
  960. hasGeodeticSurfaceNormals,
  961. exaggeration,
  962. exaggerationRelativeHeight,
  963. );
  964. // At _most_, we have vertices for the 4 corners, plus 1 center, plus every adjacent edge vertex.
  965. // In reality there will be less most of the time, but close enough; better
  966. // to overestimate than to re-allocate/copy/traverse the vertices twice.
  967. // Also, we'll often be able to squeeze the index data into the extra space in the buffer.
  968. let maxVertexCount = 5;
  969. let meshes;
  970. meshes = fill.westMeshes;
  971. for (i = 0, len = meshes.length; i < len; ++i) {
  972. maxVertexCount += meshes[i].eastIndicesNorthToSouth.length;
  973. }
  974. meshes = fill.southMeshes;
  975. for (i = 0, len = meshes.length; i < len; ++i) {
  976. maxVertexCount += meshes[i].northIndicesWestToEast.length;
  977. }
  978. meshes = fill.eastMeshes;
  979. for (i = 0, len = meshes.length; i < len; ++i) {
  980. maxVertexCount += meshes[i].westIndicesSouthToNorth.length;
  981. }
  982. meshes = fill.northMeshes;
  983. for (i = 0, len = meshes.length; i < len; ++i) {
  984. maxVertexCount += meshes[i].southIndicesEastToWest.length;
  985. }
  986. const heightRange = heightRangeScratch;
  987. heightRange.minimumHeight = minimumHeight;
  988. heightRange.maximumHeight = maximumHeight;
  989. const stride = encoding.stride;
  990. let typedArray = new Float32Array(maxVertexCount * stride);
  991. let nextIndex = 0;
  992. const northwestIndex = nextIndex;
  993. nextIndex = addVertexWithComputedPosition(
  994. ellipsoid,
  995. rectangle,
  996. encoding,
  997. typedArray,
  998. nextIndex,
  999. 0.0,
  1000. 1.0,
  1001. nwCorner.height,
  1002. nwCorner.encodedNormal,
  1003. 1.0,
  1004. heightRange,
  1005. );
  1006. nextIndex = addEdge(
  1007. fill,
  1008. ellipsoid,
  1009. encoding,
  1010. typedArray,
  1011. nextIndex,
  1012. fill.westTiles,
  1013. fill.westMeshes,
  1014. TileEdge.EAST,
  1015. heightRange,
  1016. );
  1017. const southwestIndex = nextIndex;
  1018. nextIndex = addVertexWithComputedPosition(
  1019. ellipsoid,
  1020. rectangle,
  1021. encoding,
  1022. typedArray,
  1023. nextIndex,
  1024. 0.0,
  1025. 0.0,
  1026. swCorner.height,
  1027. swCorner.encodedNormal,
  1028. 0.0,
  1029. heightRange,
  1030. );
  1031. nextIndex = addEdge(
  1032. fill,
  1033. ellipsoid,
  1034. encoding,
  1035. typedArray,
  1036. nextIndex,
  1037. fill.southTiles,
  1038. fill.southMeshes,
  1039. TileEdge.NORTH,
  1040. heightRange,
  1041. );
  1042. const southeastIndex = nextIndex;
  1043. nextIndex = addVertexWithComputedPosition(
  1044. ellipsoid,
  1045. rectangle,
  1046. encoding,
  1047. typedArray,
  1048. nextIndex,
  1049. 1.0,
  1050. 0.0,
  1051. seCorner.height,
  1052. seCorner.encodedNormal,
  1053. 0.0,
  1054. heightRange,
  1055. );
  1056. nextIndex = addEdge(
  1057. fill,
  1058. ellipsoid,
  1059. encoding,
  1060. typedArray,
  1061. nextIndex,
  1062. fill.eastTiles,
  1063. fill.eastMeshes,
  1064. TileEdge.WEST,
  1065. heightRange,
  1066. );
  1067. const northeastIndex = nextIndex;
  1068. nextIndex = addVertexWithComputedPosition(
  1069. ellipsoid,
  1070. rectangle,
  1071. encoding,
  1072. typedArray,
  1073. nextIndex,
  1074. 1.0,
  1075. 1.0,
  1076. neCorner.height,
  1077. neCorner.encodedNormal,
  1078. 1.0,
  1079. heightRange,
  1080. );
  1081. nextIndex = addEdge(
  1082. fill,
  1083. ellipsoid,
  1084. encoding,
  1085. typedArray,
  1086. nextIndex,
  1087. fill.northTiles,
  1088. fill.northMeshes,
  1089. TileEdge.SOUTH,
  1090. heightRange,
  1091. );
  1092. minimumHeight = heightRange.minimumHeight;
  1093. maximumHeight = heightRange.maximumHeight;
  1094. const obb = OrientedBoundingBox.fromRectangle(
  1095. rectangle,
  1096. minimumHeight,
  1097. maximumHeight,
  1098. tile.tilingScheme.ellipsoid,
  1099. );
  1100. // Add a single vertex at the center of the tile.
  1101. const southMercatorY =
  1102. WebMercatorProjection.geodeticLatitudeToMercatorAngle(rectangle.south);
  1103. const oneOverMercatorHeight =
  1104. 1.0 /
  1105. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(rectangle.north) -
  1106. southMercatorY);
  1107. const centerWebMercatorT =
  1108. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  1109. centerCartographic.latitude,
  1110. ) -
  1111. southMercatorY) *
  1112. oneOverMercatorHeight;
  1113. const geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic(
  1114. cartographicScratch,
  1115. normalScratch,
  1116. );
  1117. const centerEncodedNormal = AttributeCompression.octEncode(
  1118. geodeticSurfaceNormal,
  1119. octEncodedNormalScratch,
  1120. );
  1121. const centerIndex = nextIndex;
  1122. encoding.encode(
  1123. typedArray,
  1124. nextIndex * stride,
  1125. obb.center,
  1126. Cartesian2.fromElements(0.5, 0.5, uvScratch),
  1127. middleHeight,
  1128. centerEncodedNormal,
  1129. centerWebMercatorT,
  1130. geodeticSurfaceNormal,
  1131. );
  1132. ++nextIndex;
  1133. const vertexCount = nextIndex;
  1134. const bytesPerIndex = vertexCount < 256 ? 1 : 2;
  1135. const indexCount = (vertexCount - 1) * 3; // one triangle per edge vertex
  1136. const indexDataBytes = indexCount * bytesPerIndex;
  1137. const availableBytesInBuffer =
  1138. (typedArray.length - vertexCount * stride) *
  1139. Float32Array.BYTES_PER_ELEMENT;
  1140. let indices;
  1141. if (availableBytesInBuffer >= indexDataBytes) {
  1142. // Store the index data in the same buffer as the vertex data.
  1143. const startIndex = vertexCount * stride * Float32Array.BYTES_PER_ELEMENT;
  1144. indices =
  1145. vertexCount < 256
  1146. ? new Uint8Array(typedArray.buffer, startIndex, indexCount)
  1147. : new Uint16Array(typedArray.buffer, startIndex, indexCount);
  1148. } else {
  1149. // Allocate a new buffer for the index data.
  1150. indices =
  1151. vertexCount < 256
  1152. ? new Uint8Array(indexCount)
  1153. : new Uint16Array(indexCount);
  1154. }
  1155. typedArray = new Float32Array(typedArray.buffer, 0, vertexCount * stride);
  1156. let indexOut = 0;
  1157. for (i = 0; i < vertexCount - 2; ++i) {
  1158. indices[indexOut++] = centerIndex;
  1159. indices[indexOut++] = i;
  1160. indices[indexOut++] = i + 1;
  1161. }
  1162. indices[indexOut++] = centerIndex;
  1163. indices[indexOut++] = i;
  1164. indices[indexOut++] = 0;
  1165. const westIndicesSouthToNorth = [];
  1166. for (i = southwestIndex; i >= northwestIndex; --i) {
  1167. westIndicesSouthToNorth.push(i);
  1168. }
  1169. const southIndicesEastToWest = [];
  1170. for (i = southeastIndex; i >= southwestIndex; --i) {
  1171. southIndicesEastToWest.push(i);
  1172. }
  1173. const eastIndicesNorthToSouth = [];
  1174. for (i = northeastIndex; i >= southeastIndex; --i) {
  1175. eastIndicesNorthToSouth.push(i);
  1176. }
  1177. const northIndicesWestToEast = [];
  1178. northIndicesWestToEast.push(0);
  1179. for (i = centerIndex - 1; i >= northeastIndex; --i) {
  1180. northIndicesWestToEast.push(i);
  1181. }
  1182. fill.mesh = new TerrainMesh(
  1183. encoding.center,
  1184. typedArray,
  1185. indices,
  1186. indexCount,
  1187. vertexCount,
  1188. minimumHeight,
  1189. maximumHeight,
  1190. rectangle,
  1191. BoundingSphere.fromOrientedBoundingBox(obb),
  1192. computeOccludeePoint(
  1193. tileProvider,
  1194. obb.center,
  1195. rectangle,
  1196. minimumHeight,
  1197. maximumHeight,
  1198. ),
  1199. encoding.stride,
  1200. obb,
  1201. encoding,
  1202. westIndicesSouthToNorth,
  1203. southIndicesEastToWest,
  1204. eastIndicesNorthToSouth,
  1205. northIndicesWestToEast,
  1206. );
  1207. }
  1208. const context = frameState.context;
  1209. fill._destroyVertexArray(vertexArraysToDestroy);
  1210. fill.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  1211. context,
  1212. fill.mesh,
  1213. );
  1214. surfaceTile.processImagery(
  1215. tile,
  1216. tileProvider.terrainProvider,
  1217. frameState,
  1218. true,
  1219. );
  1220. const oldTexture = fill.waterMaskTexture;
  1221. fill.waterMaskTexture = undefined;
  1222. if (tileProvider.terrainProvider.hasWaterMask) {
  1223. const waterSourceTile = surfaceTile._findAncestorTileWithTerrainData(tile);
  1224. if (
  1225. defined(waterSourceTile) &&
  1226. defined(waterSourceTile.data.waterMaskTexture)
  1227. ) {
  1228. fill.waterMaskTexture = waterSourceTile.data.waterMaskTexture;
  1229. ++fill.waterMaskTexture.referenceCount;
  1230. surfaceTile._computeWaterMaskTranslationAndScale(
  1231. tile,
  1232. waterSourceTile,
  1233. fill.waterMaskTranslationAndScale,
  1234. );
  1235. }
  1236. }
  1237. if (defined(oldTexture)) {
  1238. --oldTexture.referenceCount;
  1239. if (oldTexture.referenceCount === 0) {
  1240. oldTexture.destroy();
  1241. }
  1242. }
  1243. }
  1244. function addVertexWithComputedPosition(
  1245. ellipsoid,
  1246. rectangle,
  1247. encoding,
  1248. buffer,
  1249. index,
  1250. u,
  1251. v,
  1252. height,
  1253. encodedNormal,
  1254. webMercatorT,
  1255. heightRange,
  1256. ) {
  1257. const cartographic = cartographicScratch;
  1258. cartographic.longitude = CesiumMath.lerp(rectangle.west, rectangle.east, u);
  1259. cartographic.latitude = CesiumMath.lerp(rectangle.south, rectangle.north, v);
  1260. cartographic.height = height;
  1261. const position = ellipsoid.cartographicToCartesian(
  1262. cartographic,
  1263. cartesianScratch,
  1264. );
  1265. let geodeticSurfaceNormal;
  1266. if (encoding.hasGeodeticSurfaceNormals) {
  1267. geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
  1268. position,
  1269. normalScratch,
  1270. );
  1271. }
  1272. const uv = uvScratch2;
  1273. uv.x = u;
  1274. uv.y = v;
  1275. encoding.encode(
  1276. buffer,
  1277. index * encoding.stride,
  1278. position,
  1279. uv,
  1280. height,
  1281. encodedNormal,
  1282. webMercatorT,
  1283. geodeticSurfaceNormal,
  1284. );
  1285. heightRange.minimumHeight = Math.min(heightRange.minimumHeight, height);
  1286. heightRange.maximumHeight = Math.max(heightRange.maximumHeight, height);
  1287. return index + 1;
  1288. }
  1289. const sourceRectangleScratch = new Rectangle();
  1290. function transformTextureCoordinates(
  1291. sourceTile,
  1292. targetTile,
  1293. coordinates,
  1294. result,
  1295. ) {
  1296. let sourceRectangle = sourceTile.rectangle;
  1297. const targetRectangle = targetTile.rectangle;
  1298. // Handle transforming across the anti-meridian.
  1299. if (
  1300. targetTile.x === 0 &&
  1301. coordinates.x === 1.0 &&
  1302. sourceTile.x ===
  1303. sourceTile.tilingScheme.getNumberOfXTilesAtLevel(sourceTile.level) - 1
  1304. ) {
  1305. sourceRectangle = Rectangle.clone(
  1306. sourceTile.rectangle,
  1307. sourceRectangleScratch,
  1308. );
  1309. sourceRectangle.west -= CesiumMath.TWO_PI;
  1310. sourceRectangle.east -= CesiumMath.TWO_PI;
  1311. } else if (
  1312. sourceTile.x === 0 &&
  1313. coordinates.x === 0.0 &&
  1314. targetTile.x ===
  1315. targetTile.tilingScheme.getNumberOfXTilesAtLevel(targetTile.level) - 1
  1316. ) {
  1317. sourceRectangle = Rectangle.clone(
  1318. sourceTile.rectangle,
  1319. sourceRectangleScratch,
  1320. );
  1321. sourceRectangle.west += CesiumMath.TWO_PI;
  1322. sourceRectangle.east += CesiumMath.TWO_PI;
  1323. }
  1324. const sourceWidth = sourceRectangle.east - sourceRectangle.west;
  1325. const umin = (targetRectangle.west - sourceRectangle.west) / sourceWidth;
  1326. const umax = (targetRectangle.east - sourceRectangle.west) / sourceWidth;
  1327. const sourceHeight = sourceRectangle.north - sourceRectangle.south;
  1328. const vmin = (targetRectangle.south - sourceRectangle.south) / sourceHeight;
  1329. const vmax = (targetRectangle.north - sourceRectangle.south) / sourceHeight;
  1330. let u = (coordinates.x - umin) / (umax - umin);
  1331. let v = (coordinates.y - vmin) / (vmax - vmin);
  1332. // Ensure that coordinates very near the corners are at the corners.
  1333. if (Math.abs(u) < Math.EPSILON5) {
  1334. u = 0.0;
  1335. } else if (Math.abs(u - 1.0) < Math.EPSILON5) {
  1336. u = 1.0;
  1337. }
  1338. if (Math.abs(v) < Math.EPSILON5) {
  1339. v = 0.0;
  1340. } else if (Math.abs(v - 1.0) < Math.EPSILON5) {
  1341. v = 1.0;
  1342. }
  1343. result.x = u;
  1344. result.y = v;
  1345. return result;
  1346. }
  1347. const encodedNormalScratch = new Cartesian2();
  1348. function getVertexFromTileAtCorner(sourceMesh, sourceIndex, u, v, vertex) {
  1349. const sourceEncoding = sourceMesh.encoding;
  1350. const sourceVertices = sourceMesh.vertices;
  1351. vertex.height = sourceEncoding.decodeHeight(sourceVertices, sourceIndex);
  1352. if (sourceEncoding.hasVertexNormals) {
  1353. sourceEncoding.getOctEncodedNormal(
  1354. sourceVertices,
  1355. sourceIndex,
  1356. vertex.encodedNormal,
  1357. );
  1358. } else {
  1359. const normal = vertex.encodedNormal;
  1360. normal.x = 0.0;
  1361. normal.y = 0.0;
  1362. }
  1363. }
  1364. const encodedNormalScratch2 = new Cartesian2();
  1365. const cartesianScratch2 = new Cartesian3();
  1366. function getInterpolatedVertexAtCorner(
  1367. ellipsoid,
  1368. sourceTile,
  1369. targetTile,
  1370. sourceMesh,
  1371. previousIndex,
  1372. nextIndex,
  1373. u,
  1374. v,
  1375. interpolateU,
  1376. vertex,
  1377. ) {
  1378. const sourceEncoding = sourceMesh.encoding;
  1379. const sourceVertices = sourceMesh.vertices;
  1380. const previousUv = transformTextureCoordinates(
  1381. sourceTile,
  1382. targetTile,
  1383. sourceEncoding.decodeTextureCoordinates(
  1384. sourceVertices,
  1385. previousIndex,
  1386. uvScratch,
  1387. ),
  1388. uvScratch,
  1389. );
  1390. const nextUv = transformTextureCoordinates(
  1391. sourceTile,
  1392. targetTile,
  1393. sourceEncoding.decodeTextureCoordinates(
  1394. sourceVertices,
  1395. nextIndex,
  1396. uvScratch2,
  1397. ),
  1398. uvScratch2,
  1399. );
  1400. let ratio;
  1401. if (interpolateU) {
  1402. ratio = (u - previousUv.x) / (nextUv.x - previousUv.x);
  1403. } else {
  1404. ratio = (v - previousUv.y) / (nextUv.y - previousUv.y);
  1405. }
  1406. const height1 = sourceEncoding.decodeHeight(sourceVertices, previousIndex);
  1407. const height2 = sourceEncoding.decodeHeight(sourceVertices, nextIndex);
  1408. const targetRectangle = targetTile.rectangle;
  1409. cartographicScratch.longitude = CesiumMath.lerp(
  1410. targetRectangle.west,
  1411. targetRectangle.east,
  1412. u,
  1413. );
  1414. cartographicScratch.latitude = CesiumMath.lerp(
  1415. targetRectangle.south,
  1416. targetRectangle.north,
  1417. v,
  1418. );
  1419. vertex.height = cartographicScratch.height = CesiumMath.lerp(
  1420. height1,
  1421. height2,
  1422. ratio,
  1423. );
  1424. let normal;
  1425. if (sourceEncoding.hasVertexNormals) {
  1426. const encodedNormal1 = sourceEncoding.getOctEncodedNormal(
  1427. sourceVertices,
  1428. previousIndex,
  1429. encodedNormalScratch,
  1430. );
  1431. const encodedNormal2 = sourceEncoding.getOctEncodedNormal(
  1432. sourceVertices,
  1433. nextIndex,
  1434. encodedNormalScratch2,
  1435. );
  1436. const normal1 = AttributeCompression.octDecode(
  1437. encodedNormal1.x,
  1438. encodedNormal1.y,
  1439. cartesianScratch,
  1440. );
  1441. const normal2 = AttributeCompression.octDecode(
  1442. encodedNormal2.x,
  1443. encodedNormal2.y,
  1444. cartesianScratch2,
  1445. );
  1446. normal = Cartesian3.lerp(normal1, normal2, ratio, cartesianScratch);
  1447. Cartesian3.normalize(normal, normal);
  1448. AttributeCompression.octEncode(normal, vertex.encodedNormal);
  1449. } else {
  1450. normal = ellipsoid.geodeticSurfaceNormalCartographic(
  1451. cartographicScratch,
  1452. cartesianScratch,
  1453. );
  1454. AttributeCompression.octEncode(normal, vertex.encodedNormal);
  1455. }
  1456. }
  1457. function getVertexWithHeightAtCorner(
  1458. terrainFillMesh,
  1459. ellipsoid,
  1460. u,
  1461. v,
  1462. height,
  1463. vertex,
  1464. ) {
  1465. vertex.height = height;
  1466. const normal = ellipsoid.geodeticSurfaceNormalCartographic(
  1467. cartographicScratch,
  1468. cartesianScratch,
  1469. );
  1470. AttributeCompression.octEncode(normal, vertex.encodedNormal);
  1471. }
  1472. function getCorner(
  1473. terrainFillMesh,
  1474. ellipsoid,
  1475. u,
  1476. v,
  1477. cornerTile,
  1478. cornerMesh,
  1479. previousEdgeTiles,
  1480. previousEdgeMeshes,
  1481. nextEdgeTiles,
  1482. nextEdgeMeshes,
  1483. vertex,
  1484. ) {
  1485. const gotCorner =
  1486. getCornerFromEdge(
  1487. terrainFillMesh,
  1488. ellipsoid,
  1489. previousEdgeMeshes,
  1490. previousEdgeTiles,
  1491. false,
  1492. u,
  1493. v,
  1494. vertex,
  1495. ) ||
  1496. getCornerFromEdge(
  1497. terrainFillMesh,
  1498. ellipsoid,
  1499. nextEdgeMeshes,
  1500. nextEdgeTiles,
  1501. true,
  1502. u,
  1503. v,
  1504. vertex,
  1505. );
  1506. if (gotCorner) {
  1507. return vertex;
  1508. }
  1509. let vertexIndex;
  1510. if (meshIsUsable(cornerTile, cornerMesh)) {
  1511. // Corner mesh is valid, copy its corner vertex to this mesh.
  1512. if (u === 0.0) {
  1513. if (v === 0.0) {
  1514. // southwest destination, northeast source
  1515. vertexIndex = cornerMesh.eastIndicesNorthToSouth[0];
  1516. } else {
  1517. // northwest destination, southeast source
  1518. vertexIndex = cornerMesh.southIndicesEastToWest[0];
  1519. }
  1520. } else if (v === 0.0) {
  1521. // southeast destination, northwest source
  1522. vertexIndex = cornerMesh.northIndicesWestToEast[0];
  1523. } else {
  1524. // northeast destination, southwest source
  1525. vertexIndex = cornerMesh.westIndicesSouthToNorth[0];
  1526. }
  1527. getVertexFromTileAtCorner(cornerMesh, vertexIndex, u, v, vertex);
  1528. return vertex;
  1529. }
  1530. // There is no precise vertex available from the corner or from either adjacent edge.
  1531. // This is either because there are no tiles at all at the edges and corner, or
  1532. // because the tiles at the edge are higher-level-number and don't extend all the way
  1533. // to the corner.
  1534. // Try to grab a height from the adjacent edges.
  1535. let height;
  1536. if (u === 0.0) {
  1537. if (v === 0.0) {
  1538. // southwest
  1539. height = getClosestHeightToCorner(
  1540. terrainFillMesh.westMeshes,
  1541. terrainFillMesh.westTiles,
  1542. TileEdge.EAST,
  1543. terrainFillMesh.southMeshes,
  1544. terrainFillMesh.southTiles,
  1545. TileEdge.NORTH,
  1546. u,
  1547. v,
  1548. );
  1549. } else {
  1550. // northwest
  1551. height = getClosestHeightToCorner(
  1552. terrainFillMesh.northMeshes,
  1553. terrainFillMesh.northTiles,
  1554. TileEdge.SOUTH,
  1555. terrainFillMesh.westMeshes,
  1556. terrainFillMesh.westTiles,
  1557. TileEdge.EAST,
  1558. u,
  1559. v,
  1560. );
  1561. }
  1562. } else if (v === 0.0) {
  1563. // southeast
  1564. height = getClosestHeightToCorner(
  1565. terrainFillMesh.southMeshes,
  1566. terrainFillMesh.southTiles,
  1567. TileEdge.NORTH,
  1568. terrainFillMesh.eastMeshes,
  1569. terrainFillMesh.eastTiles,
  1570. TileEdge.WEST,
  1571. u,
  1572. v,
  1573. );
  1574. } else {
  1575. // northeast
  1576. height = getClosestHeightToCorner(
  1577. terrainFillMesh.eastMeshes,
  1578. terrainFillMesh.eastTiles,
  1579. TileEdge.WEST,
  1580. terrainFillMesh.northMeshes,
  1581. terrainFillMesh.northTiles,
  1582. TileEdge.SOUTH,
  1583. u,
  1584. v,
  1585. );
  1586. }
  1587. if (defined(height)) {
  1588. getVertexWithHeightAtCorner(
  1589. terrainFillMesh,
  1590. ellipsoid,
  1591. u,
  1592. v,
  1593. height,
  1594. vertex,
  1595. );
  1596. return vertex;
  1597. }
  1598. // No heights available that are closer than the adjacent corners.
  1599. return undefined;
  1600. }
  1601. function getClosestHeightToCorner(
  1602. previousMeshes,
  1603. previousTiles,
  1604. previousEdge,
  1605. nextMeshes,
  1606. nextTiles,
  1607. nextEdge,
  1608. u,
  1609. v,
  1610. ) {
  1611. const height1 = getNearestHeightOnEdge(
  1612. previousMeshes,
  1613. previousTiles,
  1614. false,
  1615. previousEdge,
  1616. u,
  1617. v,
  1618. );
  1619. const height2 = getNearestHeightOnEdge(
  1620. nextMeshes,
  1621. nextTiles,
  1622. true,
  1623. nextEdge,
  1624. u,
  1625. v,
  1626. );
  1627. if (defined(height1) && defined(height2)) {
  1628. // It would be slightly better to do a weighted average of the two heights
  1629. // based on their distance from the corner, but it shouldn't matter much in practice.
  1630. return (height1 + height2) * 0.5;
  1631. } else if (defined(height1)) {
  1632. return height1;
  1633. }
  1634. return height2;
  1635. }
  1636. function addEdge(
  1637. terrainFillMesh,
  1638. ellipsoid,
  1639. encoding,
  1640. typedArray,
  1641. nextIndex,
  1642. edgeTiles,
  1643. edgeMeshes,
  1644. tileEdge,
  1645. heightRange,
  1646. ) {
  1647. for (let i = 0; i < edgeTiles.length; ++i) {
  1648. nextIndex = addEdgeMesh(
  1649. terrainFillMesh,
  1650. ellipsoid,
  1651. encoding,
  1652. typedArray,
  1653. nextIndex,
  1654. edgeTiles[i],
  1655. edgeMeshes[i],
  1656. tileEdge,
  1657. heightRange,
  1658. );
  1659. }
  1660. return nextIndex;
  1661. }
  1662. function addEdgeMesh(
  1663. terrainFillMesh,
  1664. ellipsoid,
  1665. encoding,
  1666. typedArray,
  1667. nextIndex,
  1668. edgeTile,
  1669. edgeMesh,
  1670. tileEdge,
  1671. heightRange,
  1672. ) {
  1673. // Handle copying edges across the anti-meridian.
  1674. let sourceRectangle = edgeTile.rectangle;
  1675. if (tileEdge === TileEdge.EAST && terrainFillMesh.tile.x === 0) {
  1676. sourceRectangle = Rectangle.clone(
  1677. edgeTile.rectangle,
  1678. sourceRectangleScratch,
  1679. );
  1680. sourceRectangle.west -= CesiumMath.TWO_PI;
  1681. sourceRectangle.east -= CesiumMath.TWO_PI;
  1682. } else if (tileEdge === TileEdge.WEST && edgeTile.x === 0) {
  1683. sourceRectangle = Rectangle.clone(
  1684. edgeTile.rectangle,
  1685. sourceRectangleScratch,
  1686. );
  1687. sourceRectangle.west += CesiumMath.TWO_PI;
  1688. sourceRectangle.east += CesiumMath.TWO_PI;
  1689. }
  1690. const targetRectangle = terrainFillMesh.tile.rectangle;
  1691. let lastU;
  1692. let lastV;
  1693. if (nextIndex > 0) {
  1694. encoding.decodeTextureCoordinates(typedArray, nextIndex - 1, uvScratch);
  1695. lastU = uvScratch.x;
  1696. lastV = uvScratch.y;
  1697. }
  1698. let indices;
  1699. let compareU;
  1700. switch (tileEdge) {
  1701. case TileEdge.WEST:
  1702. indices = edgeMesh.westIndicesSouthToNorth;
  1703. compareU = false;
  1704. break;
  1705. case TileEdge.NORTH:
  1706. indices = edgeMesh.northIndicesWestToEast;
  1707. compareU = true;
  1708. break;
  1709. case TileEdge.EAST:
  1710. indices = edgeMesh.eastIndicesNorthToSouth;
  1711. compareU = false;
  1712. break;
  1713. case TileEdge.SOUTH:
  1714. indices = edgeMesh.southIndicesEastToWest;
  1715. compareU = true;
  1716. break;
  1717. }
  1718. const sourceTile = edgeTile;
  1719. const targetTile = terrainFillMesh.tile;
  1720. const sourceEncoding = edgeMesh.encoding;
  1721. const sourceVertices = edgeMesh.vertices;
  1722. const targetStride = encoding.stride;
  1723. let southMercatorY;
  1724. let oneOverMercatorHeight;
  1725. if (sourceEncoding.hasWebMercatorT) {
  1726. southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  1727. targetRectangle.south,
  1728. );
  1729. oneOverMercatorHeight =
  1730. 1.0 /
  1731. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  1732. targetRectangle.north,
  1733. ) -
  1734. southMercatorY);
  1735. }
  1736. for (let i = 0; i < indices.length; ++i) {
  1737. const index = indices[i];
  1738. const uv = sourceEncoding.decodeTextureCoordinates(
  1739. sourceVertices,
  1740. index,
  1741. uvScratch,
  1742. );
  1743. transformTextureCoordinates(sourceTile, targetTile, uv, uv);
  1744. const u = uv.x;
  1745. const v = uv.y;
  1746. const uOrV = compareU ? u : v;
  1747. if (uOrV < 0.0 || uOrV > 1.0) {
  1748. // Vertex is outside the target tile - skip it.
  1749. continue;
  1750. }
  1751. if (
  1752. Math.abs(u - lastU) < CesiumMath.EPSILON5 &&
  1753. Math.abs(v - lastV) < CesiumMath.EPSILON5
  1754. ) {
  1755. // Vertex is very close to the previous one - skip it.
  1756. continue;
  1757. }
  1758. const nearlyEdgeU =
  1759. Math.abs(u) < CesiumMath.EPSILON5 ||
  1760. Math.abs(u - 1.0) < CesiumMath.EPSILON5;
  1761. const nearlyEdgeV =
  1762. Math.abs(v) < CesiumMath.EPSILON5 ||
  1763. Math.abs(v - 1.0) < CesiumMath.EPSILON5;
  1764. if (nearlyEdgeU && nearlyEdgeV) {
  1765. // Corner vertex - skip it.
  1766. continue;
  1767. }
  1768. const position = sourceEncoding.decodePosition(
  1769. sourceVertices,
  1770. index,
  1771. cartesianScratch,
  1772. );
  1773. const height = sourceEncoding.decodeHeight(sourceVertices, index);
  1774. let normal;
  1775. if (sourceEncoding.hasVertexNormals) {
  1776. normal = sourceEncoding.getOctEncodedNormal(
  1777. sourceVertices,
  1778. index,
  1779. octEncodedNormalScratch,
  1780. );
  1781. } else {
  1782. normal = octEncodedNormalScratch;
  1783. normal.x = 0.0;
  1784. normal.y = 0.0;
  1785. }
  1786. let webMercatorT = v;
  1787. if (sourceEncoding.hasWebMercatorT) {
  1788. const latitude = CesiumMath.lerp(
  1789. targetRectangle.south,
  1790. targetRectangle.north,
  1791. v,
  1792. );
  1793. webMercatorT =
  1794. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(latitude) -
  1795. southMercatorY) *
  1796. oneOverMercatorHeight;
  1797. }
  1798. let geodeticSurfaceNormal;
  1799. if (encoding.hasGeodeticSurfaceNormals) {
  1800. geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
  1801. position,
  1802. normalScratch,
  1803. );
  1804. }
  1805. encoding.encode(
  1806. typedArray,
  1807. nextIndex * targetStride,
  1808. position,
  1809. uv,
  1810. height,
  1811. normal,
  1812. webMercatorT,
  1813. geodeticSurfaceNormal,
  1814. );
  1815. heightRange.minimumHeight = Math.min(heightRange.minimumHeight, height);
  1816. heightRange.maximumHeight = Math.max(heightRange.maximumHeight, height);
  1817. ++nextIndex;
  1818. }
  1819. return nextIndex;
  1820. }
  1821. function getNearestHeightOnEdge(meshes, tiles, isNext, edge, u, v) {
  1822. let meshStart;
  1823. let meshEnd;
  1824. let meshStep;
  1825. if (isNext) {
  1826. meshStart = 0;
  1827. meshEnd = meshes.length;
  1828. meshStep = 1;
  1829. } else {
  1830. meshStart = meshes.length - 1;
  1831. meshEnd = -1;
  1832. meshStep = -1;
  1833. }
  1834. for (
  1835. let meshIndex = meshStart;
  1836. meshIndex !== meshEnd;
  1837. meshIndex += meshStep
  1838. ) {
  1839. const mesh = meshes[meshIndex];
  1840. const tile = tiles[meshIndex];
  1841. if (!meshIsUsable(tile, mesh)) {
  1842. continue;
  1843. }
  1844. let indices;
  1845. switch (edge) {
  1846. case TileEdge.WEST:
  1847. indices = mesh.westIndicesSouthToNorth;
  1848. break;
  1849. case TileEdge.SOUTH:
  1850. indices = mesh.southIndicesEastToWest;
  1851. break;
  1852. case TileEdge.EAST:
  1853. indices = mesh.eastIndicesNorthToSouth;
  1854. break;
  1855. case TileEdge.NORTH:
  1856. indices = mesh.northIndicesWestToEast;
  1857. break;
  1858. }
  1859. const index = indices[isNext ? 0 : indices.length - 1];
  1860. if (defined(index)) {
  1861. return mesh.encoding.decodeHeight(mesh.vertices, index);
  1862. }
  1863. }
  1864. return undefined;
  1865. }
  1866. function meshIsUsable(tile, mesh) {
  1867. return (
  1868. defined(mesh) &&
  1869. (!defined(tile.data.fill) || !tile.data.fill.changedThisFrame)
  1870. );
  1871. }
  1872. function getCornerFromEdge(
  1873. terrainFillMesh,
  1874. ellipsoid,
  1875. edgeMeshes,
  1876. edgeTiles,
  1877. isNext,
  1878. u,
  1879. v,
  1880. vertex,
  1881. ) {
  1882. let edgeVertices;
  1883. let compareU;
  1884. let increasing;
  1885. let vertexIndexIndex;
  1886. let vertexIndex;
  1887. const sourceTile = edgeTiles[isNext ? 0 : edgeMeshes.length - 1];
  1888. const sourceMesh = edgeMeshes[isNext ? 0 : edgeMeshes.length - 1];
  1889. if (meshIsUsable(sourceTile, sourceMesh)) {
  1890. // Previous mesh is valid, but we don't know yet if it covers this corner.
  1891. if (u === 0.0) {
  1892. if (v === 0.0) {
  1893. // southwest
  1894. edgeVertices = isNext
  1895. ? sourceMesh.northIndicesWestToEast
  1896. : sourceMesh.eastIndicesNorthToSouth;
  1897. compareU = isNext;
  1898. increasing = isNext;
  1899. } else {
  1900. // northwest
  1901. edgeVertices = isNext
  1902. ? sourceMesh.eastIndicesNorthToSouth
  1903. : sourceMesh.southIndicesEastToWest;
  1904. compareU = !isNext;
  1905. increasing = false;
  1906. }
  1907. } else if (v === 0.0) {
  1908. // southeast
  1909. edgeVertices = isNext
  1910. ? sourceMesh.westIndicesSouthToNorth
  1911. : sourceMesh.northIndicesWestToEast;
  1912. compareU = !isNext;
  1913. increasing = true;
  1914. } else {
  1915. // northeast
  1916. edgeVertices = isNext
  1917. ? sourceMesh.southIndicesEastToWest
  1918. : sourceMesh.westIndicesSouthToNorth;
  1919. compareU = isNext;
  1920. increasing = !isNext;
  1921. }
  1922. if (edgeVertices.length > 0) {
  1923. // The vertex we want will very often be the first/last vertex so check that first.
  1924. vertexIndexIndex = isNext ? 0 : edgeVertices.length - 1;
  1925. vertexIndex = edgeVertices[vertexIndexIndex];
  1926. sourceMesh.encoding.decodeTextureCoordinates(
  1927. sourceMesh.vertices,
  1928. vertexIndex,
  1929. uvScratch,
  1930. );
  1931. const targetUv = transformTextureCoordinates(
  1932. sourceTile,
  1933. terrainFillMesh.tile,
  1934. uvScratch,
  1935. uvScratch,
  1936. );
  1937. if (targetUv.x === u && targetUv.y === v) {
  1938. // Vertex is good!
  1939. getVertexFromTileAtCorner(sourceMesh, vertexIndex, u, v, vertex);
  1940. return true;
  1941. }
  1942. // The last vertex is not the one we need, try binary searching for the right one.
  1943. vertexIndexIndex = binarySearch(
  1944. edgeVertices,
  1945. compareU ? u : v,
  1946. function (vertexIndex, textureCoordinate) {
  1947. sourceMesh.encoding.decodeTextureCoordinates(
  1948. sourceMesh.vertices,
  1949. vertexIndex,
  1950. uvScratch,
  1951. );
  1952. const targetUv = transformTextureCoordinates(
  1953. sourceTile,
  1954. terrainFillMesh.tile,
  1955. uvScratch,
  1956. uvScratch,
  1957. );
  1958. if (increasing) {
  1959. if (compareU) {
  1960. return targetUv.x - u;
  1961. }
  1962. return targetUv.y - v;
  1963. } else if (compareU) {
  1964. return u - targetUv.x;
  1965. }
  1966. return v - targetUv.y;
  1967. },
  1968. );
  1969. if (vertexIndexIndex < 0) {
  1970. vertexIndexIndex = ~vertexIndexIndex;
  1971. if (vertexIndexIndex > 0 && vertexIndexIndex < edgeVertices.length) {
  1972. // The corner falls between two vertices, so interpolate between them.
  1973. getInterpolatedVertexAtCorner(
  1974. ellipsoid,
  1975. sourceTile,
  1976. terrainFillMesh.tile,
  1977. sourceMesh,
  1978. edgeVertices[vertexIndexIndex - 1],
  1979. edgeVertices[vertexIndexIndex],
  1980. u,
  1981. v,
  1982. compareU,
  1983. vertex,
  1984. );
  1985. return true;
  1986. }
  1987. } else {
  1988. // Found a vertex that fits in the corner exactly.
  1989. getVertexFromTileAtCorner(
  1990. sourceMesh,
  1991. edgeVertices[vertexIndexIndex],
  1992. u,
  1993. v,
  1994. vertex,
  1995. );
  1996. return true;
  1997. }
  1998. }
  1999. }
  2000. return false;
  2001. }
  2002. const cornerPositionsScratch = [
  2003. new Cartesian3(),
  2004. new Cartesian3(),
  2005. new Cartesian3(),
  2006. new Cartesian3(),
  2007. ];
  2008. function computeOccludeePoint(
  2009. tileProvider,
  2010. center,
  2011. rectangle,
  2012. minimumHeight,
  2013. maximumHeight,
  2014. result,
  2015. ) {
  2016. const ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
  2017. const ellipsoid = ellipsoidalOccluder.ellipsoid;
  2018. const cornerPositions = cornerPositionsScratch;
  2019. Cartesian3.fromRadians(
  2020. rectangle.west,
  2021. rectangle.south,
  2022. maximumHeight,
  2023. ellipsoid,
  2024. cornerPositions[0],
  2025. );
  2026. Cartesian3.fromRadians(
  2027. rectangle.east,
  2028. rectangle.south,
  2029. maximumHeight,
  2030. ellipsoid,
  2031. cornerPositions[1],
  2032. );
  2033. Cartesian3.fromRadians(
  2034. rectangle.west,
  2035. rectangle.north,
  2036. maximumHeight,
  2037. ellipsoid,
  2038. cornerPositions[2],
  2039. );
  2040. Cartesian3.fromRadians(
  2041. rectangle.east,
  2042. rectangle.north,
  2043. maximumHeight,
  2044. ellipsoid,
  2045. cornerPositions[3],
  2046. );
  2047. return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(
  2048. center,
  2049. cornerPositions,
  2050. minimumHeight,
  2051. result,
  2052. );
  2053. }
  2054. export default TerrainFillMesh;