TerrainFillMesh.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220
  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.terrainExaggeration;
  783. const exaggerationRelativeHeight =
  784. frameState.terrainExaggerationRelativeHeight;
  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 = exaggerationRelativeHeight;
  939. fill.mesh = terrainData._createMeshSync(createMeshSyncOptions);
  940. } else {
  941. const hasGeodeticSurfaceNormals = hasExaggeration;
  942. const centerCartographic = Rectangle.center(
  943. rectangle,
  944. centerCartographicScratch
  945. );
  946. centerCartographic.height = middleHeight;
  947. const center = ellipsoid.cartographicToCartesian(
  948. centerCartographic,
  949. scratchCenter
  950. );
  951. const encoding = new TerrainEncoding(
  952. center,
  953. undefined,
  954. undefined,
  955. undefined,
  956. undefined,
  957. true,
  958. true,
  959. hasGeodeticSurfaceNormals,
  960. exaggeration,
  961. exaggerationRelativeHeight
  962. );
  963. // At _most_, we have vertices for the 4 corners, plus 1 center, plus every adjacent edge vertex.
  964. // In reality there will be less most of the time, but close enough; better
  965. // to overestimate than to re-allocate/copy/traverse the vertices twice.
  966. // Also, we'll often be able to squeeze the index data into the extra space in the buffer.
  967. let maxVertexCount = 5;
  968. let meshes;
  969. meshes = fill.westMeshes;
  970. for (i = 0, len = meshes.length; i < len; ++i) {
  971. maxVertexCount += meshes[i].eastIndicesNorthToSouth.length;
  972. }
  973. meshes = fill.southMeshes;
  974. for (i = 0, len = meshes.length; i < len; ++i) {
  975. maxVertexCount += meshes[i].northIndicesWestToEast.length;
  976. }
  977. meshes = fill.eastMeshes;
  978. for (i = 0, len = meshes.length; i < len; ++i) {
  979. maxVertexCount += meshes[i].westIndicesSouthToNorth.length;
  980. }
  981. meshes = fill.northMeshes;
  982. for (i = 0, len = meshes.length; i < len; ++i) {
  983. maxVertexCount += meshes[i].southIndicesEastToWest.length;
  984. }
  985. const heightRange = heightRangeScratch;
  986. heightRange.minimumHeight = minimumHeight;
  987. heightRange.maximumHeight = maximumHeight;
  988. const stride = encoding.stride;
  989. let typedArray = new Float32Array(maxVertexCount * stride);
  990. let nextIndex = 0;
  991. const northwestIndex = nextIndex;
  992. nextIndex = addVertexWithComputedPosition(
  993. ellipsoid,
  994. rectangle,
  995. encoding,
  996. typedArray,
  997. nextIndex,
  998. 0.0,
  999. 1.0,
  1000. nwCorner.height,
  1001. nwCorner.encodedNormal,
  1002. 1.0,
  1003. heightRange
  1004. );
  1005. nextIndex = addEdge(
  1006. fill,
  1007. ellipsoid,
  1008. encoding,
  1009. typedArray,
  1010. nextIndex,
  1011. fill.westTiles,
  1012. fill.westMeshes,
  1013. TileEdge.EAST,
  1014. heightRange
  1015. );
  1016. const southwestIndex = nextIndex;
  1017. nextIndex = addVertexWithComputedPosition(
  1018. ellipsoid,
  1019. rectangle,
  1020. encoding,
  1021. typedArray,
  1022. nextIndex,
  1023. 0.0,
  1024. 0.0,
  1025. swCorner.height,
  1026. swCorner.encodedNormal,
  1027. 0.0,
  1028. heightRange
  1029. );
  1030. nextIndex = addEdge(
  1031. fill,
  1032. ellipsoid,
  1033. encoding,
  1034. typedArray,
  1035. nextIndex,
  1036. fill.southTiles,
  1037. fill.southMeshes,
  1038. TileEdge.NORTH,
  1039. heightRange
  1040. );
  1041. const southeastIndex = nextIndex;
  1042. nextIndex = addVertexWithComputedPosition(
  1043. ellipsoid,
  1044. rectangle,
  1045. encoding,
  1046. typedArray,
  1047. nextIndex,
  1048. 1.0,
  1049. 0.0,
  1050. seCorner.height,
  1051. seCorner.encodedNormal,
  1052. 0.0,
  1053. heightRange
  1054. );
  1055. nextIndex = addEdge(
  1056. fill,
  1057. ellipsoid,
  1058. encoding,
  1059. typedArray,
  1060. nextIndex,
  1061. fill.eastTiles,
  1062. fill.eastMeshes,
  1063. TileEdge.WEST,
  1064. heightRange
  1065. );
  1066. const northeastIndex = nextIndex;
  1067. nextIndex = addVertexWithComputedPosition(
  1068. ellipsoid,
  1069. rectangle,
  1070. encoding,
  1071. typedArray,
  1072. nextIndex,
  1073. 1.0,
  1074. 1.0,
  1075. neCorner.height,
  1076. neCorner.encodedNormal,
  1077. 1.0,
  1078. heightRange
  1079. );
  1080. nextIndex = addEdge(
  1081. fill,
  1082. ellipsoid,
  1083. encoding,
  1084. typedArray,
  1085. nextIndex,
  1086. fill.northTiles,
  1087. fill.northMeshes,
  1088. TileEdge.SOUTH,
  1089. heightRange
  1090. );
  1091. minimumHeight = heightRange.minimumHeight;
  1092. maximumHeight = heightRange.maximumHeight;
  1093. const obb = OrientedBoundingBox.fromRectangle(
  1094. rectangle,
  1095. minimumHeight,
  1096. maximumHeight,
  1097. tile.tilingScheme.ellipsoid
  1098. );
  1099. // Add a single vertex at the center of the tile.
  1100. const southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  1101. rectangle.south
  1102. );
  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. BoundingSphere.fromOrientedBoundingBox(obb),
  1191. computeOccludeePoint(
  1192. tileProvider,
  1193. obb.center,
  1194. rectangle,
  1195. minimumHeight,
  1196. maximumHeight
  1197. ),
  1198. encoding.stride,
  1199. obb,
  1200. encoding,
  1201. westIndicesSouthToNorth,
  1202. southIndicesEastToWest,
  1203. eastIndicesNorthToSouth,
  1204. northIndicesWestToEast
  1205. );
  1206. }
  1207. const context = frameState.context;
  1208. fill._destroyVertexArray(vertexArraysToDestroy);
  1209. fill.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  1210. context,
  1211. fill.mesh
  1212. );
  1213. surfaceTile.processImagery(
  1214. tile,
  1215. tileProvider.terrainProvider,
  1216. frameState,
  1217. true
  1218. );
  1219. const oldTexture = fill.waterMaskTexture;
  1220. fill.waterMaskTexture = undefined;
  1221. if (tileProvider.terrainProvider.hasWaterMask) {
  1222. const waterSourceTile = surfaceTile._findAncestorTileWithTerrainData(tile);
  1223. if (
  1224. defined(waterSourceTile) &&
  1225. defined(waterSourceTile.data.waterMaskTexture)
  1226. ) {
  1227. fill.waterMaskTexture = waterSourceTile.data.waterMaskTexture;
  1228. ++fill.waterMaskTexture.referenceCount;
  1229. surfaceTile._computeWaterMaskTranslationAndScale(
  1230. tile,
  1231. waterSourceTile,
  1232. fill.waterMaskTranslationAndScale
  1233. );
  1234. }
  1235. }
  1236. if (defined(oldTexture)) {
  1237. --oldTexture.referenceCount;
  1238. if (oldTexture.referenceCount === 0) {
  1239. oldTexture.destroy();
  1240. }
  1241. }
  1242. }
  1243. function addVertexWithComputedPosition(
  1244. ellipsoid,
  1245. rectangle,
  1246. encoding,
  1247. buffer,
  1248. index,
  1249. u,
  1250. v,
  1251. height,
  1252. encodedNormal,
  1253. webMercatorT,
  1254. heightRange
  1255. ) {
  1256. const cartographic = cartographicScratch;
  1257. cartographic.longitude = CesiumMath.lerp(rectangle.west, rectangle.east, u);
  1258. cartographic.latitude = CesiumMath.lerp(rectangle.south, rectangle.north, v);
  1259. cartographic.height = height;
  1260. const position = ellipsoid.cartographicToCartesian(
  1261. cartographic,
  1262. cartesianScratch
  1263. );
  1264. let geodeticSurfaceNormal;
  1265. if (encoding.hasGeodeticSurfaceNormals) {
  1266. geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
  1267. position,
  1268. normalScratch
  1269. );
  1270. }
  1271. const uv = uvScratch2;
  1272. uv.x = u;
  1273. uv.y = v;
  1274. encoding.encode(
  1275. buffer,
  1276. index * encoding.stride,
  1277. position,
  1278. uv,
  1279. height,
  1280. encodedNormal,
  1281. webMercatorT,
  1282. geodeticSurfaceNormal
  1283. );
  1284. heightRange.minimumHeight = Math.min(heightRange.minimumHeight, height);
  1285. heightRange.maximumHeight = Math.max(heightRange.maximumHeight, height);
  1286. return index + 1;
  1287. }
  1288. const sourceRectangleScratch = new Rectangle();
  1289. function transformTextureCoordinates(
  1290. sourceTile,
  1291. targetTile,
  1292. coordinates,
  1293. result
  1294. ) {
  1295. let sourceRectangle = sourceTile.rectangle;
  1296. const targetRectangle = targetTile.rectangle;
  1297. // Handle transforming across the anti-meridian.
  1298. if (
  1299. targetTile.x === 0 &&
  1300. coordinates.x === 1.0 &&
  1301. sourceTile.x ===
  1302. sourceTile.tilingScheme.getNumberOfXTilesAtLevel(sourceTile.level) - 1
  1303. ) {
  1304. sourceRectangle = Rectangle.clone(
  1305. sourceTile.rectangle,
  1306. sourceRectangleScratch
  1307. );
  1308. sourceRectangle.west -= CesiumMath.TWO_PI;
  1309. sourceRectangle.east -= CesiumMath.TWO_PI;
  1310. } else if (
  1311. sourceTile.x === 0 &&
  1312. coordinates.x === 0.0 &&
  1313. targetTile.x ===
  1314. targetTile.tilingScheme.getNumberOfXTilesAtLevel(targetTile.level) - 1
  1315. ) {
  1316. sourceRectangle = Rectangle.clone(
  1317. sourceTile.rectangle,
  1318. sourceRectangleScratch
  1319. );
  1320. sourceRectangle.west += CesiumMath.TWO_PI;
  1321. sourceRectangle.east += CesiumMath.TWO_PI;
  1322. }
  1323. const sourceWidth = sourceRectangle.east - sourceRectangle.west;
  1324. const umin = (targetRectangle.west - sourceRectangle.west) / sourceWidth;
  1325. const umax = (targetRectangle.east - sourceRectangle.west) / sourceWidth;
  1326. const sourceHeight = sourceRectangle.north - sourceRectangle.south;
  1327. const vmin = (targetRectangle.south - sourceRectangle.south) / sourceHeight;
  1328. const vmax = (targetRectangle.north - sourceRectangle.south) / sourceHeight;
  1329. let u = (coordinates.x - umin) / (umax - umin);
  1330. let v = (coordinates.y - vmin) / (vmax - vmin);
  1331. // Ensure that coordinates very near the corners are at the corners.
  1332. if (Math.abs(u) < Math.EPSILON5) {
  1333. u = 0.0;
  1334. } else if (Math.abs(u - 1.0) < Math.EPSILON5) {
  1335. u = 1.0;
  1336. }
  1337. if (Math.abs(v) < Math.EPSILON5) {
  1338. v = 0.0;
  1339. } else if (Math.abs(v - 1.0) < Math.EPSILON5) {
  1340. v = 1.0;
  1341. }
  1342. result.x = u;
  1343. result.y = v;
  1344. return result;
  1345. }
  1346. const encodedNormalScratch = new Cartesian2();
  1347. function getVertexFromTileAtCorner(sourceMesh, sourceIndex, u, v, vertex) {
  1348. const sourceEncoding = sourceMesh.encoding;
  1349. const sourceVertices = sourceMesh.vertices;
  1350. vertex.height = sourceEncoding.decodeHeight(sourceVertices, sourceIndex);
  1351. if (sourceEncoding.hasVertexNormals) {
  1352. sourceEncoding.getOctEncodedNormal(
  1353. sourceVertices,
  1354. sourceIndex,
  1355. vertex.encodedNormal
  1356. );
  1357. } else {
  1358. const normal = vertex.encodedNormal;
  1359. normal.x = 0.0;
  1360. normal.y = 0.0;
  1361. }
  1362. }
  1363. const encodedNormalScratch2 = new Cartesian2();
  1364. const cartesianScratch2 = new Cartesian3();
  1365. function getInterpolatedVertexAtCorner(
  1366. ellipsoid,
  1367. sourceTile,
  1368. targetTile,
  1369. sourceMesh,
  1370. previousIndex,
  1371. nextIndex,
  1372. u,
  1373. v,
  1374. interpolateU,
  1375. vertex
  1376. ) {
  1377. const sourceEncoding = sourceMesh.encoding;
  1378. const sourceVertices = sourceMesh.vertices;
  1379. const previousUv = transformTextureCoordinates(
  1380. sourceTile,
  1381. targetTile,
  1382. sourceEncoding.decodeTextureCoordinates(
  1383. sourceVertices,
  1384. previousIndex,
  1385. uvScratch
  1386. ),
  1387. uvScratch
  1388. );
  1389. const nextUv = transformTextureCoordinates(
  1390. sourceTile,
  1391. targetTile,
  1392. sourceEncoding.decodeTextureCoordinates(
  1393. sourceVertices,
  1394. nextIndex,
  1395. uvScratch2
  1396. ),
  1397. uvScratch2
  1398. );
  1399. let ratio;
  1400. if (interpolateU) {
  1401. ratio = (u - previousUv.x) / (nextUv.x - previousUv.x);
  1402. } else {
  1403. ratio = (v - previousUv.y) / (nextUv.y - previousUv.y);
  1404. }
  1405. const height1 = sourceEncoding.decodeHeight(sourceVertices, previousIndex);
  1406. const height2 = sourceEncoding.decodeHeight(sourceVertices, nextIndex);
  1407. const targetRectangle = targetTile.rectangle;
  1408. cartographicScratch.longitude = CesiumMath.lerp(
  1409. targetRectangle.west,
  1410. targetRectangle.east,
  1411. u
  1412. );
  1413. cartographicScratch.latitude = CesiumMath.lerp(
  1414. targetRectangle.south,
  1415. targetRectangle.north,
  1416. v
  1417. );
  1418. vertex.height = cartographicScratch.height = CesiumMath.lerp(
  1419. height1,
  1420. height2,
  1421. ratio
  1422. );
  1423. let normal;
  1424. if (sourceEncoding.hasVertexNormals) {
  1425. const encodedNormal1 = sourceEncoding.getOctEncodedNormal(
  1426. sourceVertices,
  1427. previousIndex,
  1428. encodedNormalScratch
  1429. );
  1430. const encodedNormal2 = sourceEncoding.getOctEncodedNormal(
  1431. sourceVertices,
  1432. nextIndex,
  1433. encodedNormalScratch2
  1434. );
  1435. const normal1 = AttributeCompression.octDecode(
  1436. encodedNormal1.x,
  1437. encodedNormal1.y,
  1438. cartesianScratch
  1439. );
  1440. const normal2 = AttributeCompression.octDecode(
  1441. encodedNormal2.x,
  1442. encodedNormal2.y,
  1443. cartesianScratch2
  1444. );
  1445. normal = Cartesian3.lerp(normal1, normal2, ratio, cartesianScratch);
  1446. Cartesian3.normalize(normal, normal);
  1447. AttributeCompression.octEncode(normal, vertex.encodedNormal);
  1448. } else {
  1449. normal = ellipsoid.geodeticSurfaceNormalCartographic(
  1450. cartographicScratch,
  1451. cartesianScratch
  1452. );
  1453. AttributeCompression.octEncode(normal, vertex.encodedNormal);
  1454. }
  1455. }
  1456. function getVertexWithHeightAtCorner(
  1457. terrainFillMesh,
  1458. ellipsoid,
  1459. u,
  1460. v,
  1461. height,
  1462. vertex
  1463. ) {
  1464. vertex.height = height;
  1465. const normal = ellipsoid.geodeticSurfaceNormalCartographic(
  1466. cartographicScratch,
  1467. cartesianScratch
  1468. );
  1469. AttributeCompression.octEncode(normal, vertex.encodedNormal);
  1470. }
  1471. function getCorner(
  1472. terrainFillMesh,
  1473. ellipsoid,
  1474. u,
  1475. v,
  1476. cornerTile,
  1477. cornerMesh,
  1478. previousEdgeTiles,
  1479. previousEdgeMeshes,
  1480. nextEdgeTiles,
  1481. nextEdgeMeshes,
  1482. vertex
  1483. ) {
  1484. const gotCorner =
  1485. getCornerFromEdge(
  1486. terrainFillMesh,
  1487. ellipsoid,
  1488. previousEdgeMeshes,
  1489. previousEdgeTiles,
  1490. false,
  1491. u,
  1492. v,
  1493. vertex
  1494. ) ||
  1495. getCornerFromEdge(
  1496. terrainFillMesh,
  1497. ellipsoid,
  1498. nextEdgeMeshes,
  1499. nextEdgeTiles,
  1500. true,
  1501. u,
  1502. v,
  1503. vertex
  1504. );
  1505. if (gotCorner) {
  1506. return vertex;
  1507. }
  1508. let vertexIndex;
  1509. if (meshIsUsable(cornerTile, cornerMesh)) {
  1510. // Corner mesh is valid, copy its corner vertex to this mesh.
  1511. if (u === 0.0) {
  1512. if (v === 0.0) {
  1513. // southwest destination, northeast source
  1514. vertexIndex = cornerMesh.eastIndicesNorthToSouth[0];
  1515. } else {
  1516. // northwest destination, southeast source
  1517. vertexIndex = cornerMesh.southIndicesEastToWest[0];
  1518. }
  1519. } else if (v === 0.0) {
  1520. // southeast destination, northwest source
  1521. vertexIndex = cornerMesh.northIndicesWestToEast[0];
  1522. } else {
  1523. // northeast destination, southwest source
  1524. vertexIndex = cornerMesh.westIndicesSouthToNorth[0];
  1525. }
  1526. getVertexFromTileAtCorner(cornerMesh, vertexIndex, u, v, vertex);
  1527. return vertex;
  1528. }
  1529. // There is no precise vertex available from the corner or from either adjacent edge.
  1530. // This is either because there are no tiles at all at the edges and corner, or
  1531. // because the tiles at the edge are higher-level-number and don't extend all the way
  1532. // to the corner.
  1533. // Try to grab a height from the adjacent edges.
  1534. let height;
  1535. if (u === 0.0) {
  1536. if (v === 0.0) {
  1537. // southwest
  1538. height = getClosestHeightToCorner(
  1539. terrainFillMesh.westMeshes,
  1540. terrainFillMesh.westTiles,
  1541. TileEdge.EAST,
  1542. terrainFillMesh.southMeshes,
  1543. terrainFillMesh.southTiles,
  1544. TileEdge.NORTH,
  1545. u,
  1546. v
  1547. );
  1548. } else {
  1549. // northwest
  1550. height = getClosestHeightToCorner(
  1551. terrainFillMesh.northMeshes,
  1552. terrainFillMesh.northTiles,
  1553. TileEdge.SOUTH,
  1554. terrainFillMesh.westMeshes,
  1555. terrainFillMesh.westTiles,
  1556. TileEdge.EAST,
  1557. u,
  1558. v
  1559. );
  1560. }
  1561. } else if (v === 0.0) {
  1562. // southeast
  1563. height = getClosestHeightToCorner(
  1564. terrainFillMesh.southMeshes,
  1565. terrainFillMesh.southTiles,
  1566. TileEdge.NORTH,
  1567. terrainFillMesh.eastMeshes,
  1568. terrainFillMesh.eastTiles,
  1569. TileEdge.WEST,
  1570. u,
  1571. v
  1572. );
  1573. } else {
  1574. // northeast
  1575. height = getClosestHeightToCorner(
  1576. terrainFillMesh.eastMeshes,
  1577. terrainFillMesh.eastTiles,
  1578. TileEdge.WEST,
  1579. terrainFillMesh.northMeshes,
  1580. terrainFillMesh.northTiles,
  1581. TileEdge.SOUTH,
  1582. u,
  1583. v
  1584. );
  1585. }
  1586. if (defined(height)) {
  1587. getVertexWithHeightAtCorner(
  1588. terrainFillMesh,
  1589. ellipsoid,
  1590. u,
  1591. v,
  1592. height,
  1593. vertex
  1594. );
  1595. return vertex;
  1596. }
  1597. // No heights available that are closer than the adjacent corners.
  1598. return undefined;
  1599. }
  1600. function getClosestHeightToCorner(
  1601. previousMeshes,
  1602. previousTiles,
  1603. previousEdge,
  1604. nextMeshes,
  1605. nextTiles,
  1606. nextEdge,
  1607. u,
  1608. v
  1609. ) {
  1610. const height1 = getNearestHeightOnEdge(
  1611. previousMeshes,
  1612. previousTiles,
  1613. false,
  1614. previousEdge,
  1615. u,
  1616. v
  1617. );
  1618. const height2 = getNearestHeightOnEdge(
  1619. nextMeshes,
  1620. nextTiles,
  1621. true,
  1622. nextEdge,
  1623. u,
  1624. v
  1625. );
  1626. if (defined(height1) && defined(height2)) {
  1627. // It would be slightly better to do a weighted average of the two heights
  1628. // based on their distance from the corner, but it shouldn't matter much in practice.
  1629. return (height1 + height2) * 0.5;
  1630. } else if (defined(height1)) {
  1631. return height1;
  1632. }
  1633. return height2;
  1634. }
  1635. function addEdge(
  1636. terrainFillMesh,
  1637. ellipsoid,
  1638. encoding,
  1639. typedArray,
  1640. nextIndex,
  1641. edgeTiles,
  1642. edgeMeshes,
  1643. tileEdge,
  1644. heightRange
  1645. ) {
  1646. for (let i = 0; i < edgeTiles.length; ++i) {
  1647. nextIndex = addEdgeMesh(
  1648. terrainFillMesh,
  1649. ellipsoid,
  1650. encoding,
  1651. typedArray,
  1652. nextIndex,
  1653. edgeTiles[i],
  1654. edgeMeshes[i],
  1655. tileEdge,
  1656. heightRange
  1657. );
  1658. }
  1659. return nextIndex;
  1660. }
  1661. function addEdgeMesh(
  1662. terrainFillMesh,
  1663. ellipsoid,
  1664. encoding,
  1665. typedArray,
  1666. nextIndex,
  1667. edgeTile,
  1668. edgeMesh,
  1669. tileEdge,
  1670. heightRange
  1671. ) {
  1672. // Handle copying edges across the anti-meridian.
  1673. let sourceRectangle = edgeTile.rectangle;
  1674. if (tileEdge === TileEdge.EAST && terrainFillMesh.tile.x === 0) {
  1675. sourceRectangle = Rectangle.clone(
  1676. edgeTile.rectangle,
  1677. sourceRectangleScratch
  1678. );
  1679. sourceRectangle.west -= CesiumMath.TWO_PI;
  1680. sourceRectangle.east -= CesiumMath.TWO_PI;
  1681. } else if (tileEdge === TileEdge.WEST && edgeTile.x === 0) {
  1682. sourceRectangle = Rectangle.clone(
  1683. edgeTile.rectangle,
  1684. sourceRectangleScratch
  1685. );
  1686. sourceRectangle.west += CesiumMath.TWO_PI;
  1687. sourceRectangle.east += CesiumMath.TWO_PI;
  1688. }
  1689. const targetRectangle = terrainFillMesh.tile.rectangle;
  1690. let lastU;
  1691. let lastV;
  1692. if (nextIndex > 0) {
  1693. encoding.decodeTextureCoordinates(typedArray, nextIndex - 1, uvScratch);
  1694. lastU = uvScratch.x;
  1695. lastV = uvScratch.y;
  1696. }
  1697. let indices;
  1698. let compareU;
  1699. switch (tileEdge) {
  1700. case TileEdge.WEST:
  1701. indices = edgeMesh.westIndicesSouthToNorth;
  1702. compareU = false;
  1703. break;
  1704. case TileEdge.NORTH:
  1705. indices = edgeMesh.northIndicesWestToEast;
  1706. compareU = true;
  1707. break;
  1708. case TileEdge.EAST:
  1709. indices = edgeMesh.eastIndicesNorthToSouth;
  1710. compareU = false;
  1711. break;
  1712. case TileEdge.SOUTH:
  1713. indices = edgeMesh.southIndicesEastToWest;
  1714. compareU = true;
  1715. break;
  1716. }
  1717. const sourceTile = edgeTile;
  1718. const targetTile = terrainFillMesh.tile;
  1719. const sourceEncoding = edgeMesh.encoding;
  1720. const sourceVertices = edgeMesh.vertices;
  1721. const targetStride = encoding.stride;
  1722. let southMercatorY;
  1723. let oneOverMercatorHeight;
  1724. if (sourceEncoding.hasWebMercatorT) {
  1725. southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  1726. targetRectangle.south
  1727. );
  1728. oneOverMercatorHeight =
  1729. 1.0 /
  1730. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(
  1731. targetRectangle.north
  1732. ) -
  1733. southMercatorY);
  1734. }
  1735. for (let i = 0; i < indices.length; ++i) {
  1736. const index = indices[i];
  1737. const uv = sourceEncoding.decodeTextureCoordinates(
  1738. sourceVertices,
  1739. index,
  1740. uvScratch
  1741. );
  1742. transformTextureCoordinates(sourceTile, targetTile, uv, uv);
  1743. const u = uv.x;
  1744. const v = uv.y;
  1745. const uOrV = compareU ? u : v;
  1746. if (uOrV < 0.0 || uOrV > 1.0) {
  1747. // Vertex is outside the target tile - skip it.
  1748. continue;
  1749. }
  1750. if (
  1751. Math.abs(u - lastU) < CesiumMath.EPSILON5 &&
  1752. Math.abs(v - lastV) < CesiumMath.EPSILON5
  1753. ) {
  1754. // Vertex is very close to the previous one - skip it.
  1755. continue;
  1756. }
  1757. const nearlyEdgeU =
  1758. Math.abs(u) < CesiumMath.EPSILON5 ||
  1759. Math.abs(u - 1.0) < CesiumMath.EPSILON5;
  1760. const nearlyEdgeV =
  1761. Math.abs(v) < CesiumMath.EPSILON5 ||
  1762. Math.abs(v - 1.0) < CesiumMath.EPSILON5;
  1763. if (nearlyEdgeU && nearlyEdgeV) {
  1764. // Corner vertex - skip it.
  1765. continue;
  1766. }
  1767. const position = sourceEncoding.decodePosition(
  1768. sourceVertices,
  1769. index,
  1770. cartesianScratch
  1771. );
  1772. const height = sourceEncoding.decodeHeight(sourceVertices, index);
  1773. let normal;
  1774. if (sourceEncoding.hasVertexNormals) {
  1775. normal = sourceEncoding.getOctEncodedNormal(
  1776. sourceVertices,
  1777. index,
  1778. octEncodedNormalScratch
  1779. );
  1780. } else {
  1781. normal = octEncodedNormalScratch;
  1782. normal.x = 0.0;
  1783. normal.y = 0.0;
  1784. }
  1785. let webMercatorT = v;
  1786. if (sourceEncoding.hasWebMercatorT) {
  1787. const latitude = CesiumMath.lerp(
  1788. targetRectangle.south,
  1789. targetRectangle.north,
  1790. v
  1791. );
  1792. webMercatorT =
  1793. (WebMercatorProjection.geodeticLatitudeToMercatorAngle(latitude) -
  1794. southMercatorY) *
  1795. oneOverMercatorHeight;
  1796. }
  1797. let geodeticSurfaceNormal;
  1798. if (encoding.hasGeodeticSurfaceNormals) {
  1799. geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
  1800. position,
  1801. normalScratch
  1802. );
  1803. }
  1804. encoding.encode(
  1805. typedArray,
  1806. nextIndex * targetStride,
  1807. position,
  1808. uv,
  1809. height,
  1810. normal,
  1811. webMercatorT,
  1812. geodeticSurfaceNormal
  1813. );
  1814. heightRange.minimumHeight = Math.min(heightRange.minimumHeight, height);
  1815. heightRange.maximumHeight = Math.max(heightRange.maximumHeight, height);
  1816. ++nextIndex;
  1817. }
  1818. return nextIndex;
  1819. }
  1820. function getNearestHeightOnEdge(meshes, tiles, isNext, edge, u, v) {
  1821. let meshStart;
  1822. let meshEnd;
  1823. let meshStep;
  1824. if (isNext) {
  1825. meshStart = 0;
  1826. meshEnd = meshes.length;
  1827. meshStep = 1;
  1828. } else {
  1829. meshStart = meshes.length - 1;
  1830. meshEnd = -1;
  1831. meshStep = -1;
  1832. }
  1833. for (
  1834. let meshIndex = meshStart;
  1835. meshIndex !== meshEnd;
  1836. meshIndex += meshStep
  1837. ) {
  1838. const mesh = meshes[meshIndex];
  1839. const tile = tiles[meshIndex];
  1840. if (!meshIsUsable(tile, mesh)) {
  1841. continue;
  1842. }
  1843. let indices;
  1844. switch (edge) {
  1845. case TileEdge.WEST:
  1846. indices = mesh.westIndicesSouthToNorth;
  1847. break;
  1848. case TileEdge.SOUTH:
  1849. indices = mesh.southIndicesEastToWest;
  1850. break;
  1851. case TileEdge.EAST:
  1852. indices = mesh.eastIndicesNorthToSouth;
  1853. break;
  1854. case TileEdge.NORTH:
  1855. indices = mesh.northIndicesWestToEast;
  1856. break;
  1857. }
  1858. const index = indices[isNext ? 0 : indices.length - 1];
  1859. if (defined(index)) {
  1860. return mesh.encoding.decodeHeight(mesh.vertices, index);
  1861. }
  1862. }
  1863. return undefined;
  1864. }
  1865. function meshIsUsable(tile, mesh) {
  1866. return (
  1867. defined(mesh) &&
  1868. (!defined(tile.data.fill) || !tile.data.fill.changedThisFrame)
  1869. );
  1870. }
  1871. function getCornerFromEdge(
  1872. terrainFillMesh,
  1873. ellipsoid,
  1874. edgeMeshes,
  1875. edgeTiles,
  1876. isNext,
  1877. u,
  1878. v,
  1879. vertex
  1880. ) {
  1881. let edgeVertices;
  1882. let compareU;
  1883. let increasing;
  1884. let vertexIndexIndex;
  1885. let vertexIndex;
  1886. const sourceTile = edgeTiles[isNext ? 0 : edgeMeshes.length - 1];
  1887. const sourceMesh = edgeMeshes[isNext ? 0 : edgeMeshes.length - 1];
  1888. if (meshIsUsable(sourceTile, sourceMesh)) {
  1889. // Previous mesh is valid, but we don't know yet if it covers this corner.
  1890. if (u === 0.0) {
  1891. if (v === 0.0) {
  1892. // southwest
  1893. edgeVertices = isNext
  1894. ? sourceMesh.northIndicesWestToEast
  1895. : sourceMesh.eastIndicesNorthToSouth;
  1896. compareU = isNext;
  1897. increasing = isNext;
  1898. } else {
  1899. // northwest
  1900. edgeVertices = isNext
  1901. ? sourceMesh.eastIndicesNorthToSouth
  1902. : sourceMesh.southIndicesEastToWest;
  1903. compareU = !isNext;
  1904. increasing = false;
  1905. }
  1906. } else if (v === 0.0) {
  1907. // southeast
  1908. edgeVertices = isNext
  1909. ? sourceMesh.westIndicesSouthToNorth
  1910. : sourceMesh.northIndicesWestToEast;
  1911. compareU = !isNext;
  1912. increasing = true;
  1913. } else {
  1914. // northeast
  1915. edgeVertices = isNext
  1916. ? sourceMesh.southIndicesEastToWest
  1917. : sourceMesh.westIndicesSouthToNorth;
  1918. compareU = isNext;
  1919. increasing = !isNext;
  1920. }
  1921. if (edgeVertices.length > 0) {
  1922. // The vertex we want will very often be the first/last vertex so check that first.
  1923. vertexIndexIndex = isNext ? 0 : edgeVertices.length - 1;
  1924. vertexIndex = edgeVertices[vertexIndexIndex];
  1925. sourceMesh.encoding.decodeTextureCoordinates(
  1926. sourceMesh.vertices,
  1927. vertexIndex,
  1928. uvScratch
  1929. );
  1930. const targetUv = transformTextureCoordinates(
  1931. sourceTile,
  1932. terrainFillMesh.tile,
  1933. uvScratch,
  1934. uvScratch
  1935. );
  1936. if (targetUv.x === u && targetUv.y === v) {
  1937. // Vertex is good!
  1938. getVertexFromTileAtCorner(sourceMesh, vertexIndex, u, v, vertex);
  1939. return true;
  1940. }
  1941. // The last vertex is not the one we need, try binary searching for the right one.
  1942. vertexIndexIndex = binarySearch(edgeVertices, compareU ? u : v, function (
  1943. vertexIndex,
  1944. textureCoordinate
  1945. ) {
  1946. sourceMesh.encoding.decodeTextureCoordinates(
  1947. sourceMesh.vertices,
  1948. vertexIndex,
  1949. uvScratch
  1950. );
  1951. const targetUv = transformTextureCoordinates(
  1952. sourceTile,
  1953. terrainFillMesh.tile,
  1954. uvScratch,
  1955. uvScratch
  1956. );
  1957. if (increasing) {
  1958. if (compareU) {
  1959. return targetUv.x - u;
  1960. }
  1961. return targetUv.y - v;
  1962. } else if (compareU) {
  1963. return u - targetUv.x;
  1964. }
  1965. return v - targetUv.y;
  1966. });
  1967. if (vertexIndexIndex < 0) {
  1968. vertexIndexIndex = ~vertexIndexIndex;
  1969. if (vertexIndexIndex > 0 && vertexIndexIndex < edgeVertices.length) {
  1970. // The corner falls between two vertices, so interpolate between them.
  1971. getInterpolatedVertexAtCorner(
  1972. ellipsoid,
  1973. sourceTile,
  1974. terrainFillMesh.tile,
  1975. sourceMesh,
  1976. edgeVertices[vertexIndexIndex - 1],
  1977. edgeVertices[vertexIndexIndex],
  1978. u,
  1979. v,
  1980. compareU,
  1981. vertex
  1982. );
  1983. return true;
  1984. }
  1985. } else {
  1986. // Found a vertex that fits in the corner exactly.
  1987. getVertexFromTileAtCorner(
  1988. sourceMesh,
  1989. edgeVertices[vertexIndexIndex],
  1990. u,
  1991. v,
  1992. vertex
  1993. );
  1994. return true;
  1995. }
  1996. }
  1997. }
  1998. return false;
  1999. }
  2000. const cornerPositionsScratch = [
  2001. new Cartesian3(),
  2002. new Cartesian3(),
  2003. new Cartesian3(),
  2004. new Cartesian3(),
  2005. ];
  2006. function computeOccludeePoint(
  2007. tileProvider,
  2008. center,
  2009. rectangle,
  2010. minimumHeight,
  2011. maximumHeight,
  2012. result
  2013. ) {
  2014. const ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
  2015. const ellipsoid = ellipsoidalOccluder.ellipsoid;
  2016. const cornerPositions = cornerPositionsScratch;
  2017. Cartesian3.fromRadians(
  2018. rectangle.west,
  2019. rectangle.south,
  2020. maximumHeight,
  2021. ellipsoid,
  2022. cornerPositions[0]
  2023. );
  2024. Cartesian3.fromRadians(
  2025. rectangle.east,
  2026. rectangle.south,
  2027. maximumHeight,
  2028. ellipsoid,
  2029. cornerPositions[1]
  2030. );
  2031. Cartesian3.fromRadians(
  2032. rectangle.west,
  2033. rectangle.north,
  2034. maximumHeight,
  2035. ellipsoid,
  2036. cornerPositions[2]
  2037. );
  2038. Cartesian3.fromRadians(
  2039. rectangle.east,
  2040. rectangle.north,
  2041. maximumHeight,
  2042. ellipsoid,
  2043. cornerPositions[3]
  2044. );
  2045. return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(
  2046. center,
  2047. cornerPositions,
  2048. minimumHeight,
  2049. result
  2050. );
  2051. }
  2052. export default TerrainFillMesh;