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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Color from "../Core/Color.js";
  4. import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
  5. import CullingVolume from "../Core/CullingVolume.js";
  6. import defined from "../Core/defined.js";
  7. import deprecationWarning from "../Core/deprecationWarning.js";
  8. import destroyObject from "../Core/destroyObject.js";
  9. import Ellipsoid from "../Core/Ellipsoid.js";
  10. import Intersect from "../Core/Intersect.js";
  11. import JulianDate from "../Core/JulianDate.js";
  12. import CesiumMath from "../Core/Math.js";
  13. import Matrix3 from "../Core/Matrix3.js";
  14. import Matrix4 from "../Core/Matrix4.js";
  15. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  16. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  17. import Rectangle from "../Core/Rectangle.js";
  18. import Request from "../Core/Request.js";
  19. import RequestScheduler from "../Core/RequestScheduler.js";
  20. import RequestState from "../Core/RequestState.js";
  21. import RequestType from "../Core/RequestType.js";
  22. import Resource from "../Core/Resource.js";
  23. import RuntimeError from "../Core/RuntimeError.js";
  24. import Cesium3DContentGroup from "./Cesium3DContentGroup.js";
  25. import Cesium3DTileContentFactory from "./Cesium3DTileContentFactory.js";
  26. import Cesium3DTileContentState from "./Cesium3DTileContentState.js";
  27. import Cesium3DTileContentType from "./Cesium3DTileContentType.js";
  28. import Cesium3DTileOptimizationHint from "./Cesium3DTileOptimizationHint.js";
  29. import Cesium3DTilePass from "./Cesium3DTilePass.js";
  30. import Cesium3DTileRefine from "./Cesium3DTileRefine.js";
  31. import Empty3DTileContent from "./Empty3DTileContent.js";
  32. import findContentMetadata from "./findContentMetadata.js";
  33. import findGroupMetadata from "./findGroupMetadata.js";
  34. import findTileMetadata from "./findTileMetadata.js";
  35. import hasExtension from "./hasExtension.js";
  36. import Multiple3DTileContent from "./Multiple3DTileContent.js";
  37. import BoundingVolumeSemantics from "./BoundingVolumeSemantics.js";
  38. import preprocess3DTileContent from "./preprocess3DTileContent.js";
  39. import SceneMode from "./SceneMode.js";
  40. import TileBoundingRegion from "./TileBoundingRegion.js";
  41. import TileBoundingS2Cell from "./TileBoundingS2Cell.js";
  42. import TileBoundingSphere from "./TileBoundingSphere.js";
  43. import TileOrientedBoundingBox from "./TileOrientedBoundingBox.js";
  44. import Pass from "../Renderer/Pass.js";
  45. import VerticalExaggeration from "../Core/VerticalExaggeration.js";
  46. /**
  47. * A tile in a {@link Cesium3DTileset}. When a tile is first created, its content is not loaded;
  48. * the content is loaded on-demand when needed based on the view.
  49. * <p>
  50. * Do not construct this directly, instead access tiles through {@link Cesium3DTileset#tileVisible}.
  51. * </p>
  52. *
  53. * @alias Cesium3DTile
  54. * @constructor
  55. * @param {Cesium3DTileset} tileset The tileset
  56. * @param {Resource} baseResource The base resource for the tileset
  57. * @param {object} header The JSON header for the tile
  58. * @param {Cesium3DTile} parent The parent tile of the new tile
  59. */
  60. function Cesium3DTile(tileset, baseResource, header, parent) {
  61. this._tileset = tileset;
  62. this._header = header;
  63. const hasContentsArray = defined(header.contents);
  64. const hasMultipleContents =
  65. (hasContentsArray && header.contents.length > 1) ||
  66. hasExtension(header, "3DTILES_multiple_contents");
  67. // In the 1.0 schema, content is stored in tile.content instead of tile.contents
  68. const contentHeader =
  69. hasContentsArray && !hasMultipleContents
  70. ? header.contents[0]
  71. : header.content;
  72. this._contentHeader = contentHeader;
  73. /**
  74. * The local transform of this tile.
  75. * @type {Matrix4}
  76. */
  77. this.transform = defined(header.transform)
  78. ? Matrix4.unpack(header.transform)
  79. : Matrix4.clone(Matrix4.IDENTITY);
  80. const parentTransform = defined(parent)
  81. ? parent.computedTransform
  82. : tileset.modelMatrix;
  83. const computedTransform = Matrix4.multiply(
  84. parentTransform,
  85. this.transform,
  86. new Matrix4(),
  87. );
  88. const parentInitialTransform = defined(parent)
  89. ? parent._initialTransform
  90. : Matrix4.IDENTITY;
  91. this._initialTransform = Matrix4.multiply(
  92. parentInitialTransform,
  93. this.transform,
  94. new Matrix4(),
  95. );
  96. /**
  97. * The final computed transform of this tile.
  98. * @type {Matrix4}
  99. * @readonly
  100. */
  101. this.computedTransform = computedTransform;
  102. /**
  103. * When tile metadata is present (3D Tiles 1.1) or the <code>3DTILES_metadata</code> extension is used,
  104. * this stores a {@link TileMetadata} object for accessing tile metadata.
  105. *
  106. * @type {TileMetadata}
  107. * @readonly
  108. * @private
  109. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  110. */
  111. this.metadata = findTileMetadata(tileset, header);
  112. this._verticalExaggeration = 1.0;
  113. this._verticalExaggerationRelativeHeight = 0.0;
  114. // Important: tile metadata must be parsed before this line so that the
  115. // metadata semantics TILE_BOUNDING_BOX, TILE_BOUNDING_REGION, or TILE_BOUNDING_SPHERE
  116. // can override header.boundingVolume (if necessary)
  117. this._boundingVolume = this.createBoundingVolume(
  118. header.boundingVolume,
  119. computedTransform,
  120. );
  121. this._boundingVolume2D = undefined;
  122. let contentBoundingVolume;
  123. if (defined(contentHeader) && defined(contentHeader.boundingVolume)) {
  124. // Non-leaf tiles may have a content bounding-volume, which is a tight-fit bounding volume
  125. // around only the features in the tile. This box is useful for culling for rendering,
  126. // but not for culling for traversing the tree since it does not guarantee spatial coherence, i.e.,
  127. // since it only bounds features in the tile, not the entire tile, children may be
  128. // outside of this box.
  129. contentBoundingVolume = this.createBoundingVolume(
  130. contentHeader.boundingVolume,
  131. computedTransform,
  132. );
  133. }
  134. this._contentBoundingVolume = contentBoundingVolume;
  135. this._contentBoundingVolume2D = undefined;
  136. let viewerRequestVolume;
  137. if (defined(header.viewerRequestVolume)) {
  138. viewerRequestVolume = this.createBoundingVolume(
  139. header.viewerRequestVolume,
  140. computedTransform,
  141. );
  142. }
  143. this._viewerRequestVolume = viewerRequestVolume;
  144. /**
  145. * The error, in meters, introduced if this tile is rendered and its children are not.
  146. * This is used to compute screen space error, i.e., the error measured in pixels.
  147. *
  148. * @type {number}
  149. * @readonly
  150. */
  151. this.geometricError = header.geometricError;
  152. this._geometricError = header.geometricError;
  153. if (!defined(this._geometricError)) {
  154. this._geometricError = defined(parent)
  155. ? parent._geometricError
  156. : tileset._geometricError;
  157. Cesium3DTile._deprecationWarning(
  158. "geometricErrorUndefined",
  159. "Required property geometricError is undefined for this tile. Using parent's geometric error instead.",
  160. );
  161. }
  162. this.updateGeometricErrorScale();
  163. let refine;
  164. if (defined(header.refine)) {
  165. if (header.refine === "replace" || header.refine === "add") {
  166. Cesium3DTile._deprecationWarning(
  167. "lowercase-refine",
  168. `This tile uses a lowercase refine "${
  169. header.refine
  170. }". Instead use "${header.refine.toUpperCase()}".`,
  171. );
  172. }
  173. refine =
  174. header.refine.toUpperCase() === "REPLACE"
  175. ? Cesium3DTileRefine.REPLACE
  176. : Cesium3DTileRefine.ADD;
  177. } else if (defined(parent)) {
  178. // Inherit from parent tile if omitted.
  179. refine = parent.refine;
  180. } else {
  181. refine = Cesium3DTileRefine.REPLACE;
  182. }
  183. /**
  184. * Specifies the type of refinement that is used when traversing this tile for rendering.
  185. *
  186. * @type {Cesium3DTileRefine}
  187. * @readonly
  188. * @private
  189. */
  190. this.refine = refine;
  191. /**
  192. * Gets the tile's children.
  193. *
  194. * @type {Cesium3DTile[]}
  195. * @readonly
  196. */
  197. this.children = [];
  198. /**
  199. * This tile's parent or <code>undefined</code> if this tile is the root.
  200. * <p>
  201. * When a tile's content points to an external tileset JSON file, the external tileset's
  202. * root tile's parent is not <code>undefined</code>; instead, the parent references
  203. * the tile (with its content pointing to an external tileset JSON file) as if the two tilesets were merged.
  204. * </p>
  205. *
  206. * @type {Cesium3DTile}
  207. * @readonly
  208. */
  209. this.parent = parent;
  210. let content;
  211. let hasEmptyContent = false;
  212. let contentState;
  213. let contentResource;
  214. let serverKey;
  215. baseResource = Resource.createIfNeeded(baseResource);
  216. if (hasMultipleContents) {
  217. contentState = Cesium3DTileContentState.UNLOADED;
  218. // Each content may have its own URI, but they all need to be resolved
  219. // relative to the tileset, so the base resource is used.
  220. contentResource = baseResource.clone();
  221. } else if (defined(contentHeader)) {
  222. let contentHeaderUri = contentHeader.uri;
  223. if (defined(contentHeader.url)) {
  224. Cesium3DTile._deprecationWarning(
  225. "contentUrl",
  226. 'This tileset JSON uses the "content.url" property which has been deprecated. Use "content.uri" instead.',
  227. );
  228. contentHeaderUri = contentHeader.url;
  229. }
  230. if (contentHeaderUri === "") {
  231. Cesium3DTile._deprecationWarning(
  232. "contentUriEmpty",
  233. "content.uri property is an empty string, which creates a circular dependency, making this tileset invalid. Omit the content property instead",
  234. );
  235. content = new Empty3DTileContent(tileset, this);
  236. hasEmptyContent = true;
  237. contentState = Cesium3DTileContentState.READY;
  238. } else {
  239. contentState = Cesium3DTileContentState.UNLOADED;
  240. contentResource = baseResource.getDerivedResource({
  241. url: contentHeaderUri,
  242. });
  243. serverKey = RequestScheduler.getServerKey(
  244. contentResource.getUrlComponent(),
  245. );
  246. }
  247. } else {
  248. content = new Empty3DTileContent(tileset, this);
  249. hasEmptyContent = true;
  250. contentState = Cesium3DTileContentState.READY;
  251. }
  252. this._content = content;
  253. this._contentResource = contentResource;
  254. this._contentState = contentState;
  255. this._expiredContent = undefined;
  256. this._serverKey = serverKey;
  257. /**
  258. * When <code>true</code>, the tile has no content.
  259. *
  260. * @type {boolean}
  261. * @readonly
  262. *
  263. * @private
  264. */
  265. this.hasEmptyContent = hasEmptyContent;
  266. /**
  267. * When <code>true</code>, the tile's content points to an external tileset.
  268. * <p>
  269. * This is <code>false</code> until the tile's content is loaded.
  270. * </p>
  271. *
  272. * @type {boolean}
  273. * @readonly
  274. *
  275. * @private
  276. */
  277. this.hasTilesetContent = false;
  278. /**
  279. * When <code>true</code>, the tile's content is an implicit tileset.
  280. * <p>
  281. * This is <code>false</code> until the tile's implicit content is loaded.
  282. * </p>
  283. *
  284. * @type {boolean}
  285. * @readonly
  286. *
  287. * @private
  288. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  289. */
  290. this.hasImplicitContent = false;
  291. /**
  292. * Determines whether the tile has renderable content.
  293. *
  294. * The loading starts with the assumption that the tile does have
  295. * renderable content, if the content is not empty.<br>
  296. * <br>
  297. * This turns <code>false</code> only when the tile content is loaded
  298. * and turns out to be a single content that points to an external
  299. * tileset or implicit content
  300. * </p>
  301. *
  302. * @type {boolean}
  303. * @readonly
  304. *
  305. * @private
  306. */
  307. this.hasRenderableContent = !hasEmptyContent;
  308. /**
  309. * When <code>true</code>, the tile contains content metadata from implicit tiling. This flag is set
  310. * for tiles transcoded by <code>Implicit3DTileContent</code>.
  311. * <p>
  312. * This is <code>false</code> until the tile's content is loaded.
  313. * </p>
  314. *
  315. * @type {boolean}
  316. * @readonly
  317. *
  318. * @private
  319. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  320. */
  321. this.hasImplicitContentMetadata = false;
  322. /**
  323. * When <code>true</code>, the tile has multiple contents, either in the tile JSON (3D Tiles 1.1)
  324. * or via the <code>3DTILES_multiple_contents</code> extension.
  325. *
  326. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_multiple_contents|3DTILES_multiple_contents extension}
  327. *
  328. * @type {boolean}
  329. * @readonly
  330. *
  331. * @private
  332. */
  333. this.hasMultipleContents = hasMultipleContents;
  334. /**
  335. * The node in the tileset's LRU cache, used to determine when to unload a tile's content.
  336. *
  337. * See {@link Cesium3DTilesetCache}
  338. *
  339. * @type {DoublyLinkedListNode}
  340. * @readonly
  341. *
  342. * @private
  343. */
  344. this.cacheNode = undefined;
  345. const expire = header.expire;
  346. let expireDuration;
  347. let expireDate;
  348. if (defined(expire)) {
  349. expireDuration = expire.duration;
  350. if (defined(expire.date)) {
  351. expireDate = JulianDate.fromIso8601(expire.date);
  352. }
  353. }
  354. /**
  355. * The time in seconds after the tile's content is ready when the content expires and new content is requested.
  356. *
  357. * @type {number}
  358. */
  359. this.expireDuration = expireDuration;
  360. /**
  361. * The date when the content expires and new content is requested.
  362. *
  363. * @type {JulianDate}
  364. */
  365. this.expireDate = expireDate;
  366. /**
  367. * The time when a style was last applied to this tile.
  368. *
  369. * @type {number}
  370. *
  371. * @private
  372. */
  373. this.lastStyleTime = 0.0;
  374. /**
  375. * Marks whether the tile's children bounds are fully contained within the tile's bounds
  376. *
  377. * @type {Cesium3DTileOptimizationHint}
  378. *
  379. * @private
  380. */
  381. this._optimChildrenWithinParent = Cesium3DTileOptimizationHint.NOT_COMPUTED;
  382. /**
  383. * Tracks if the tile's relationship with a ClippingPlaneCollection has changed with regards
  384. * to the ClippingPlaneCollection's state.
  385. *
  386. * @type {boolean}
  387. *
  388. * @private
  389. */
  390. this.clippingPlanesDirty = false;
  391. /**
  392. * Tracks if the tile's relationship with a ClippingPolygonCollection has changed with regards
  393. * to the ClippingPolygonCollection's state.
  394. *
  395. * @type {boolean}
  396. *
  397. * @private
  398. */
  399. this.clippingPolygonsDirty = false;
  400. /**
  401. * Tracks if the tile's request should be deferred until all non-deferred
  402. * tiles load.
  403. *
  404. * @type {boolean}
  405. *
  406. * @private
  407. */
  408. this.priorityDeferred = false;
  409. /**
  410. * For implicit tiling, an ImplicitTileset object will be attached to a
  411. * placeholder tile with either implicit tiling in the JSON (3D Tiles 1.1)
  412. * or the <code>3DTILES_implicit_tiling</code> extension.
  413. * This way the {@link Implicit3DTileContent} can access the tile later once the content is fetched.
  414. *
  415. * @type {ImplicitTileset|undefined}
  416. *
  417. * @private
  418. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  419. */
  420. this.implicitTileset = undefined;
  421. /**
  422. * For implicit tiling, the (level, x, y, [z]) coordinates within the
  423. * implicit tileset are stored in the tile.
  424. *
  425. * @type {ImplicitTileCoordinates|undefined}
  426. *
  427. * @private
  428. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  429. */
  430. this.implicitCoordinates = undefined;
  431. /**
  432. * For implicit tiling, each transcoded tile will hold a weak reference to
  433. * the {@link ImplicitSubtree}.
  434. *
  435. * @type {ImplicitSubtree|undefined}
  436. *
  437. * @private
  438. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  439. */
  440. this.implicitSubtree = undefined;
  441. // Members that are updated every frame for tree traversal and rendering optimizations:
  442. this._distanceToCamera = 0.0;
  443. this._centerZDepth = 0.0;
  444. this._screenSpaceError = 0.0;
  445. this._screenSpaceErrorProgressiveResolution = 0.0; // The screen space error at a given screen height of tileset.progressiveResolutionHeightFraction * screenHeight
  446. this._visibilityPlaneMask = 0;
  447. this._visible = false;
  448. this._inRequestVolume = false;
  449. this._finalResolution = true;
  450. this._depth = 0;
  451. this._stackLength = 0;
  452. this._selectionDepth = 0;
  453. this._updatedVisibilityFrame = 0;
  454. this._touchedFrame = 0;
  455. this._visitedFrame = 0;
  456. this._selectedFrame = 0;
  457. this._wasSelectedLastFrame = false;
  458. this._requestedFrame = 0;
  459. this._ancestorWithContent = undefined;
  460. this._ancestorWithContentAvailable = undefined;
  461. this._refines = false;
  462. this._shouldSelect = false;
  463. this._isClipped = true;
  464. this._isClippedByPolygon = false;
  465. this._clippingPlanesState = 0; // encapsulates (_isClipped, clippingPlanes.enabled) and number/function
  466. this._clippingPolygonsState = 0; // encapsulates (_isClipped, clippingPolygons.enabled) and number/function
  467. this._debugBoundingVolume = undefined;
  468. this._debugContentBoundingVolume = undefined;
  469. this._debugViewerRequestVolume = undefined;
  470. this._debugColor = Color.fromRandom({ alpha: 1.0 });
  471. this._debugColorizeTiles = false;
  472. this._priority = 0.0; // The priority used for request sorting
  473. this._priorityHolder = this; // Reference to the ancestor up the tree that holds the _foveatedFactor and _distanceToCamera for all tiles in the refinement chain.
  474. this._priorityProgressiveResolution = false;
  475. this._priorityProgressiveResolutionScreenSpaceErrorLeaf = false;
  476. this._priorityReverseScreenSpaceError = 0.0;
  477. this._foveatedFactor = 0.0;
  478. this._wasMinPriorityChild = false; // Needed for knowing when to continue a refinement chain. Gets reset in updateTile in traversal and gets set in updateAndPushChildren in traversal.
  479. this._loadTimestamp = new JulianDate();
  480. this._commandsLength = 0;
  481. this._color = undefined;
  482. this._colorDirty = false;
  483. this._request = undefined;
  484. }
  485. // This can be overridden for testing purposes
  486. Cesium3DTile._deprecationWarning = deprecationWarning;
  487. Object.defineProperties(Cesium3DTile.prototype, {
  488. /**
  489. * The tileset containing this tile.
  490. *
  491. * @memberof Cesium3DTile.prototype
  492. *
  493. * @type {Cesium3DTileset}
  494. * @readonly
  495. */
  496. tileset: {
  497. get: function () {
  498. return this._tileset;
  499. },
  500. },
  501. /**
  502. * The tile's content. This represents the actual tile's payload,
  503. * not the content's metadata in the tileset JSON file.
  504. *
  505. * @memberof Cesium3DTile.prototype
  506. *
  507. * @type {Cesium3DTileContent}
  508. * @readonly
  509. */
  510. content: {
  511. get: function () {
  512. return this._content;
  513. },
  514. },
  515. /**
  516. * Get the tile's bounding volume.
  517. *
  518. * @memberof Cesium3DTile.prototype
  519. *
  520. * @type {TileBoundingVolume}
  521. * @readonly
  522. * @private
  523. */
  524. boundingVolume: {
  525. get: function () {
  526. return this._boundingVolume;
  527. },
  528. },
  529. /**
  530. * Get the bounding volume of the tile's contents. This defaults to the
  531. * tile's bounding volume when the content's bounding volume is
  532. * <code>undefined</code>.
  533. *
  534. * @memberof Cesium3DTile.prototype
  535. *
  536. * @type {TileBoundingVolume}
  537. * @readonly
  538. * @private
  539. */
  540. contentBoundingVolume: {
  541. get: function () {
  542. return this._contentBoundingVolume ?? this._boundingVolume;
  543. },
  544. },
  545. /**
  546. * Get the bounding sphere derived from the tile's bounding volume.
  547. *
  548. * @memberof Cesium3DTile.prototype
  549. *
  550. * @type {BoundingSphere}
  551. * @readonly
  552. */
  553. boundingSphere: {
  554. get: function () {
  555. return this._boundingVolume.boundingSphere;
  556. },
  557. },
  558. /**
  559. * Determines if the tile is visible within the current field of view
  560. *
  561. * @memberof Cesium3DTile.prototype
  562. *
  563. * @type {boolean}
  564. * @readonly
  565. *
  566. * @private
  567. */
  568. isVisible: {
  569. get: function () {
  570. return this._visible && this._inRequestVolume;
  571. },
  572. },
  573. /**
  574. * Returns the <code>extras</code> property in the tileset JSON for this tile, which contains application specific metadata.
  575. * Returns <code>undefined</code> if <code>extras</code> does not exist.
  576. *
  577. * @memberof Cesium3DTile.prototype
  578. *
  579. * @type {object}
  580. * @readonly
  581. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification#specifying-extensions-and-application-specific-extras|Extras in the 3D Tiles specification.}
  582. */
  583. extras: {
  584. get: function () {
  585. return this._header.extras;
  586. },
  587. },
  588. /**
  589. * Gets or sets the tile's highlight color.
  590. *
  591. * @memberof Cesium3DTile.prototype
  592. *
  593. * @type {Color}
  594. *
  595. * @default {@link Color.WHITE}
  596. *
  597. * @private
  598. */
  599. color: {
  600. get: function () {
  601. if (!defined(this._color)) {
  602. this._color = new Color();
  603. }
  604. return Color.clone(this._color);
  605. },
  606. set: function (value) {
  607. this._color = Color.clone(value, this._color);
  608. this._colorDirty = true;
  609. },
  610. },
  611. /**
  612. * Determines if the tile has available content to render. <code>true</code> if the tile's
  613. * content is ready or if it has expired content that renders while new content loads; otherwise,
  614. * <code>false</code>.
  615. *
  616. * @memberof Cesium3DTile.prototype
  617. *
  618. * @type {boolean}
  619. * @readonly
  620. *
  621. * @private
  622. */
  623. contentAvailable: {
  624. get: function () {
  625. return (
  626. (this.contentReady && this.hasRenderableContent) ||
  627. (defined(this._expiredContent) && !this.contentFailed)
  628. );
  629. },
  630. },
  631. /**
  632. * Determines if the tile's content is ready. This is automatically <code>true</code> for
  633. * tile's with empty content.
  634. *
  635. * @memberof Cesium3DTile.prototype
  636. *
  637. * @type {boolean}
  638. * @readonly
  639. *
  640. * @private
  641. */
  642. contentReady: {
  643. get: function () {
  644. return this._contentState === Cesium3DTileContentState.READY;
  645. },
  646. },
  647. /**
  648. * Determines if the tile's content has not be requested. <code>true</code> if tile's
  649. * content has not be requested; otherwise, <code>false</code>.
  650. *
  651. * @memberof Cesium3DTile.prototype
  652. *
  653. * @type {boolean}
  654. * @readonly
  655. *
  656. * @private
  657. */
  658. contentUnloaded: {
  659. get: function () {
  660. return this._contentState === Cesium3DTileContentState.UNLOADED;
  661. },
  662. },
  663. /**
  664. * Determines if the tile has renderable content which is unloaded
  665. *
  666. * @memberof Cesium3DTile.prototype
  667. *
  668. * @type {boolean}
  669. * @readonly
  670. *
  671. * @private
  672. */
  673. hasUnloadedRenderableContent: {
  674. get: function () {
  675. return this.hasRenderableContent && this.contentUnloaded;
  676. },
  677. },
  678. /**
  679. * Determines if the tile's content is expired. <code>true</code> if tile's
  680. * content is expired; otherwise, <code>false</code>.
  681. *
  682. * @memberof Cesium3DTile.prototype
  683. *
  684. * @type {boolean}
  685. * @readonly
  686. *
  687. * @private
  688. */
  689. contentExpired: {
  690. get: function () {
  691. return this._contentState === Cesium3DTileContentState.EXPIRED;
  692. },
  693. },
  694. /**
  695. * Determines if the tile's content failed to load. <code>true</code> if the tile's
  696. * content failed to load; otherwise, <code>false</code>.
  697. *
  698. * @memberof Cesium3DTile.prototype
  699. *
  700. * @type {boolean}
  701. * @readonly
  702. *
  703. * @private
  704. */
  705. contentFailed: {
  706. get: function () {
  707. return this._contentState === Cesium3DTileContentState.FAILED;
  708. },
  709. },
  710. /**
  711. * Returns the number of draw commands used by this tile.
  712. *
  713. * @readonly
  714. *
  715. * @private
  716. */
  717. commandsLength: {
  718. get: function () {
  719. return this._commandsLength;
  720. },
  721. },
  722. });
  723. const scratchCartesian = new Cartesian3();
  724. /**
  725. * @private
  726. * @param {Cesium3DTile} tile
  727. * @param {FrameState} frameState
  728. * @returns {boolean}
  729. */
  730. function isPriorityDeferred(tile, frameState) {
  731. const { tileset, boundingSphere } = tile;
  732. const { radius, center } = boundingSphere;
  733. const { camera } = frameState;
  734. // If closest point on line is inside the sphere then set foveatedFactor to 0.
  735. // Otherwise, the dot product is with the line from camera to the point on the sphere that is closest to the line.
  736. const scaledCameraDirection = Cartesian3.multiplyByScalar(
  737. camera.directionWC,
  738. tile._centerZDepth,
  739. scratchCartesian,
  740. );
  741. const closestPointOnLine = Cartesian3.add(
  742. camera.positionWC,
  743. scaledCameraDirection,
  744. scratchCartesian,
  745. );
  746. // The distance from the camera's view direction to the tile.
  747. const toLine = Cartesian3.subtract(
  748. closestPointOnLine,
  749. center,
  750. scratchCartesian,
  751. );
  752. const distanceToCenterLine = Cartesian3.magnitude(toLine);
  753. const notTouchingSphere = distanceToCenterLine > radius;
  754. // If camera's direction vector is inside the bounding sphere then consider
  755. // this tile right along the line of sight and set _foveatedFactor to 0.
  756. // Otherwise,_foveatedFactor is one minus the dot product of the camera's direction
  757. // and the vector between the camera and the point on the bounding sphere closest to the view line.
  758. if (notTouchingSphere) {
  759. const toLineNormalized = Cartesian3.normalize(toLine, scratchCartesian);
  760. const scaledToLine = Cartesian3.multiplyByScalar(
  761. toLineNormalized,
  762. radius,
  763. scratchCartesian,
  764. );
  765. const closestOnSphere = Cartesian3.add(
  766. center,
  767. scaledToLine,
  768. scratchCartesian,
  769. );
  770. const toClosestOnSphere = Cartesian3.subtract(
  771. closestOnSphere,
  772. camera.positionWC,
  773. scratchCartesian,
  774. );
  775. const toClosestOnSphereNormalize = Cartesian3.normalize(
  776. toClosestOnSphere,
  777. scratchCartesian,
  778. );
  779. tile._foveatedFactor =
  780. 1.0 -
  781. Math.abs(Cartesian3.dot(camera.directionWC, toClosestOnSphereNormalize));
  782. } else {
  783. tile._foveatedFactor = 0.0;
  784. }
  785. // Skip this feature if: non-skipLevelOfDetail and replace refine, if the foveated settings are turned off, if tile is progressive resolution and replace refine and skipLevelOfDetail (will help get rid of ancestor artifacts faster)
  786. // Or if the tile is a preload of any kind
  787. const replace = tile.refine === Cesium3DTileRefine.REPLACE;
  788. const skipLevelOfDetail = tileset.isSkippingLevelOfDetail;
  789. if (
  790. (replace && !skipLevelOfDetail) ||
  791. !tileset.foveatedScreenSpaceError ||
  792. tileset.foveatedConeSize === 1.0 ||
  793. (tile._priorityProgressiveResolution && replace && skipLevelOfDetail) ||
  794. tileset._pass === Cesium3DTilePass.PRELOAD_FLIGHT ||
  795. tileset._pass === Cesium3DTilePass.PRELOAD
  796. ) {
  797. return false;
  798. }
  799. const maximumFovatedFactor = 1.0 - Math.cos(camera.frustum.fov * 0.5); // 0.14 for fov = 60. NOTE very hard to defer vertically foveated tiles since max is based on fovy (which is fov). Lowering the 0.5 to a smaller fraction of the screen height will start to defer vertically foveated tiles.
  800. const foveatedConeFactor = tileset.foveatedConeSize * maximumFovatedFactor;
  801. // If it's inside the user-defined view cone, then it should not be deferred.
  802. if (tile._foveatedFactor <= foveatedConeFactor) {
  803. return false;
  804. }
  805. // Relax SSE based on how big the angle is between the tile and the edge of the foveated cone.
  806. const range = maximumFovatedFactor - foveatedConeFactor;
  807. const normalizedFoveatedFactor = CesiumMath.clamp(
  808. (tile._foveatedFactor - foveatedConeFactor) / range,
  809. 0.0,
  810. 1.0,
  811. );
  812. const sseRelaxation = tileset.foveatedInterpolationCallback(
  813. tileset.foveatedMinimumScreenSpaceErrorRelaxation,
  814. tileset.memoryAdjustedScreenSpaceError,
  815. normalizedFoveatedFactor,
  816. );
  817. const sse =
  818. tile._screenSpaceError === 0.0 && defined(tile.parent)
  819. ? tile.parent._screenSpaceError * 0.5
  820. : tile._screenSpaceError;
  821. return tileset.memoryAdjustedScreenSpaceError - sseRelaxation <= sse;
  822. }
  823. const scratchJulianDate = new JulianDate();
  824. /**
  825. * Get the tile's screen space error.
  826. *
  827. * @private
  828. * @param {FrameState} frameState
  829. * @param {boolean} useParentGeometricError
  830. * @param {number} progressiveResolutionHeightFraction
  831. */
  832. Cesium3DTile.prototype.getScreenSpaceError = function (
  833. frameState,
  834. useParentGeometricError,
  835. progressiveResolutionHeightFraction,
  836. ) {
  837. const tileset = this._tileset;
  838. const heightFraction = progressiveResolutionHeightFraction ?? 1.0;
  839. const parentGeometricError = defined(this.parent)
  840. ? this.parent.geometricError
  841. : tileset._scaledGeometricError;
  842. const geometricError = useParentGeometricError
  843. ? parentGeometricError
  844. : this.geometricError;
  845. if (geometricError === 0.0) {
  846. // Leaf tiles do not have any error so save the computation
  847. return 0.0;
  848. }
  849. const { camera, context } = frameState;
  850. let frustum = camera.frustum;
  851. const width = context.drawingBufferWidth;
  852. const height = context.drawingBufferHeight * heightFraction;
  853. let error;
  854. if (
  855. frameState.mode === SceneMode.SCENE2D ||
  856. frustum instanceof OrthographicFrustum
  857. ) {
  858. const offCenterFrustum = frustum.offCenterFrustum;
  859. if (defined(offCenterFrustum)) {
  860. frustum = offCenterFrustum;
  861. }
  862. const pixelSize =
  863. Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) /
  864. Math.max(width, height);
  865. error = geometricError / pixelSize;
  866. } else {
  867. // Avoid divide by zero when viewer is inside the tile
  868. const distance = Math.max(this._distanceToCamera, CesiumMath.EPSILON7);
  869. const sseDenominator = frustum.sseDenominator;
  870. error = (geometricError * height) / (distance * sseDenominator);
  871. if (tileset.dynamicScreenSpaceError) {
  872. const density = tileset._dynamicScreenSpaceErrorComputedDensity;
  873. const factor = tileset.dynamicScreenSpaceErrorFactor;
  874. const dynamicError = CesiumMath.fog(distance, density) * factor;
  875. error -= dynamicError;
  876. }
  877. }
  878. error /= frameState.pixelRatio;
  879. return error;
  880. };
  881. /**
  882. * @private
  883. * @param {Cesium3DTileset} tileset
  884. * @param {Cesium3DTile} tile
  885. * @returns {boolean}
  886. */
  887. function isPriorityProgressiveResolution(tileset, tile) {
  888. if (
  889. tileset.progressiveResolutionHeightFraction <= 0.0 ||
  890. tileset.progressiveResolutionHeightFraction > 0.5
  891. ) {
  892. return false;
  893. }
  894. const maximumScreenSpaceError = tileset.memoryAdjustedScreenSpaceError;
  895. let isProgressiveResolutionTile =
  896. tile._screenSpaceErrorProgressiveResolution > maximumScreenSpaceError; // Mark non-SSE leaves
  897. tile._priorityProgressiveResolutionScreenSpaceErrorLeaf = false; // Needed for skipLOD
  898. const parent = tile.parent;
  899. const tilePasses =
  900. tile._screenSpaceErrorProgressiveResolution <= maximumScreenSpaceError;
  901. const parentFails =
  902. defined(parent) &&
  903. parent._screenSpaceErrorProgressiveResolution > maximumScreenSpaceError;
  904. if (tilePasses && parentFails) {
  905. // A progressive resolution SSE leaf, promote its priority as well
  906. tile._priorityProgressiveResolutionScreenSpaceErrorLeaf = true;
  907. isProgressiveResolutionTile = true;
  908. }
  909. return isProgressiveResolutionTile;
  910. }
  911. /**
  912. * @private
  913. * @param {Cesium3DTileset} tileset
  914. * @param {Cesium3DTile} tile
  915. * @returns {number}
  916. */
  917. function getPriorityReverseScreenSpaceError(tileset, tile) {
  918. const parent = tile.parent;
  919. const useParentScreenSpaceError =
  920. defined(parent) &&
  921. (!tileset.isSkippingLevelOfDetail ||
  922. tile._screenSpaceError === 0.0 ||
  923. parent.hasTilesetContent ||
  924. parent.hasImplicitContent);
  925. const screenSpaceError = useParentScreenSpaceError
  926. ? parent._screenSpaceError
  927. : tile._screenSpaceError;
  928. return tileset.root._screenSpaceError - screenSpaceError;
  929. }
  930. /**
  931. * Update the tile's visibility.
  932. *
  933. * @private
  934. * @param {FrameState} frameState
  935. */
  936. Cesium3DTile.prototype.updateVisibility = function (frameState) {
  937. const { parent, tileset } = this;
  938. if (this._updatedVisibilityFrame === tileset._updatedVisibilityFrame) {
  939. // The tile has already been updated for this frame
  940. return;
  941. }
  942. const parentTransform = defined(parent)
  943. ? parent.computedTransform
  944. : tileset.modelMatrix;
  945. const parentVisibilityPlaneMask = defined(parent)
  946. ? parent._visibilityPlaneMask
  947. : CullingVolume.MASK_INDETERMINATE;
  948. this.updateTransform(parentTransform, frameState);
  949. this._distanceToCamera = this.distanceToTile(frameState);
  950. this._centerZDepth = this.distanceToTileCenter(frameState);
  951. this._screenSpaceError = this.getScreenSpaceError(frameState, false);
  952. this._screenSpaceErrorProgressiveResolution = this.getScreenSpaceError(
  953. frameState,
  954. false,
  955. tileset.progressiveResolutionHeightFraction,
  956. );
  957. this._visibilityPlaneMask = this.visibility(
  958. frameState,
  959. parentVisibilityPlaneMask,
  960. ); // Use parent's plane mask to speed up visibility test
  961. this._visible = this._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE;
  962. this._inRequestVolume = this.insideViewerRequestVolume(frameState);
  963. this._priorityReverseScreenSpaceError = getPriorityReverseScreenSpaceError(
  964. tileset,
  965. this,
  966. );
  967. this._priorityProgressiveResolution = isPriorityProgressiveResolution(
  968. tileset,
  969. this,
  970. );
  971. this.priorityDeferred = isPriorityDeferred(this, frameState);
  972. this._updatedVisibilityFrame = tileset._updatedVisibilityFrame;
  973. };
  974. /**
  975. * Update whether the tile has expired.
  976. *
  977. * @private
  978. */
  979. Cesium3DTile.prototype.updateExpiration = function () {
  980. if (
  981. defined(this.expireDate) &&
  982. this.contentReady &&
  983. !this.hasEmptyContent &&
  984. !this.hasMultipleContents
  985. ) {
  986. const now = JulianDate.now(scratchJulianDate);
  987. if (JulianDate.lessThan(this.expireDate, now)) {
  988. this._contentState = Cesium3DTileContentState.EXPIRED;
  989. this._expiredContent = this._content;
  990. }
  991. }
  992. };
  993. /**
  994. * @private
  995. * @param {Cesium3DTile} tile
  996. */
  997. function updateExpireDate(tile) {
  998. if (!defined(tile.expireDuration)) {
  999. return;
  1000. }
  1001. const expireDurationDate = JulianDate.now(scratchJulianDate);
  1002. JulianDate.addSeconds(
  1003. expireDurationDate,
  1004. tile.expireDuration,
  1005. expireDurationDate,
  1006. );
  1007. if (defined(tile.expireDate)) {
  1008. if (JulianDate.lessThan(tile.expireDate, expireDurationDate)) {
  1009. JulianDate.clone(expireDurationDate, tile.expireDate);
  1010. }
  1011. } else {
  1012. tile.expireDate = JulianDate.clone(expireDurationDate);
  1013. }
  1014. }
  1015. /**
  1016. * @private
  1017. * @param {Cesium3DTile} tile
  1018. * @returns {Function}
  1019. */
  1020. function createPriorityFunction(tile) {
  1021. return function () {
  1022. return tile._priority;
  1023. };
  1024. }
  1025. /**
  1026. * Requests the tile's content.
  1027. * <p>
  1028. * The request may not be made if the Cesium Request Scheduler can't prioritize it.
  1029. * </p>
  1030. *
  1031. * @return {Promise<Cesium3DTileContent>|undefined} A promise that resolves when the request completes, or undefined if there is no request needed, or the request cannot be scheduled.
  1032. * @private
  1033. */
  1034. Cesium3DTile.prototype.requestContent = function () {
  1035. // empty contents don't require any HTTP requests
  1036. if (this.hasEmptyContent) {
  1037. return;
  1038. }
  1039. if (this.hasMultipleContents) {
  1040. return requestMultipleContents(this);
  1041. }
  1042. return requestSingleContent(this);
  1043. };
  1044. /**
  1045. * Multiple {@link Cesium3DTileContent}s are allowed within a single tile either through
  1046. * the tile JSON (3D Tiles 1.1) or the <code>3DTILES_multiple_contents</code> extension.
  1047. * Due to differences in request scheduling, this is handled separately.
  1048. * <p>
  1049. * This implementation of multiple contents does not
  1050. * support tile expiry like requestSingleContent does. If this changes,
  1051. * note that the resource.setQueryParameters() details must go inside {@link Multiple3DTileContent} since that is per-request.
  1052. * </p>
  1053. *
  1054. * @private
  1055. * @param {Cesium3DTile} tile
  1056. * @returns {Promise<Cesium3DTileContent>|Promise<undefined>|undefined} A promise that resolves to the tile content once loaded, or a promise that resolves to undefined if the request was cancelled mid-flight, or undefined if the request cannot be scheduled this frame
  1057. */
  1058. function requestMultipleContents(tile) {
  1059. let multipleContents = tile._content;
  1060. const tileset = tile._tileset;
  1061. if (!defined(multipleContents)) {
  1062. // Create the content object immediately, it will handle scheduling
  1063. // requests for inner contents.
  1064. const contentsJson = hasExtension(tile._header, "3DTILES_multiple_contents")
  1065. ? tile._header.extensions["3DTILES_multiple_contents"]
  1066. : tile._header;
  1067. multipleContents = new Multiple3DTileContent(
  1068. tileset,
  1069. tile,
  1070. tile._contentResource.clone(),
  1071. contentsJson,
  1072. );
  1073. tile._content = multipleContents;
  1074. }
  1075. const promise = multipleContents.requestInnerContents();
  1076. if (!defined(promise)) {
  1077. // Request could not all be scheduled this frame
  1078. return;
  1079. }
  1080. tile._contentState = Cesium3DTileContentState.LOADING;
  1081. return promise
  1082. .then((content) => {
  1083. if (tile.isDestroyed()) {
  1084. // Tile is unloaded before the content can process
  1085. return;
  1086. }
  1087. // Tile was canceled, try again later
  1088. if (!defined(content)) {
  1089. return;
  1090. }
  1091. tile._contentState = Cesium3DTileContentState.PROCESSING;
  1092. return multipleContents;
  1093. })
  1094. .catch((error) => {
  1095. if (tile.isDestroyed()) {
  1096. // Tile is unloaded before the content can process
  1097. return;
  1098. }
  1099. tile._contentState = Cesium3DTileContentState.FAILED;
  1100. throw error;
  1101. });
  1102. }
  1103. async function processArrayBuffer(
  1104. tile,
  1105. tileset,
  1106. request,
  1107. expired,
  1108. requestPromise,
  1109. ) {
  1110. const previousState = tile._contentState;
  1111. tile._contentState = Cesium3DTileContentState.LOADING;
  1112. ++tileset.statistics.numberOfPendingRequests;
  1113. let arrayBuffer;
  1114. try {
  1115. arrayBuffer = await requestPromise;
  1116. } catch (error) {
  1117. --tileset.statistics.numberOfPendingRequests;
  1118. if (tile.isDestroyed()) {
  1119. // Tile is unloaded before the content can process
  1120. return;
  1121. }
  1122. if (request.cancelled || request.state === RequestState.CANCELLED) {
  1123. // Cancelled due to low priority - try again later.
  1124. tile._contentState = previousState;
  1125. ++tileset.statistics.numberOfAttemptedRequests;
  1126. return;
  1127. }
  1128. if (isEmptyTile(tile, error)) {
  1129. if (expired) {
  1130. tile.expireDate = undefined;
  1131. }
  1132. tile._content = new Empty3DTileContent(tileset, tile);
  1133. markTileAsEmptyContent(tile);
  1134. tile._contentState = Cesium3DTileContentState.PROCESSING;
  1135. return tile._content;
  1136. }
  1137. tile._contentState = Cesium3DTileContentState.FAILED;
  1138. throw error;
  1139. }
  1140. if (tile.isDestroyed()) {
  1141. --tileset.statistics.numberOfPendingRequests;
  1142. // Tile is unloaded before the content can process
  1143. return;
  1144. }
  1145. if (request.cancelled || request.state === RequestState.CANCELLED) {
  1146. // Cancelled due to low priority - try again later.
  1147. tile._contentState = previousState;
  1148. --tileset.statistics.numberOfPendingRequests;
  1149. ++tileset.statistics.numberOfAttemptedRequests;
  1150. return;
  1151. }
  1152. try {
  1153. const content = await makeContent(tile, arrayBuffer);
  1154. --tileset.statistics.numberOfPendingRequests;
  1155. if (tile.isDestroyed()) {
  1156. // Tile is unloaded before the content can process
  1157. return;
  1158. }
  1159. if (expired) {
  1160. tile.expireDate = undefined;
  1161. }
  1162. tile._content = content;
  1163. if (content instanceof Empty3DTileContent) {
  1164. markTileAsEmptyContent(tile);
  1165. }
  1166. tile._contentState = Cesium3DTileContentState.PROCESSING;
  1167. return content;
  1168. } catch (error) {
  1169. --tileset.statistics.numberOfPendingRequests;
  1170. if (tile.isDestroyed()) {
  1171. // Tile is unloaded before the content can process
  1172. return;
  1173. }
  1174. tile._contentState = Cesium3DTileContentState.FAILED;
  1175. throw error;
  1176. }
  1177. }
  1178. /**
  1179. * @private
  1180. * @param {Cesium3DTile} tile
  1181. * @returns {Promise<Cesium3DTileContent>|Promise<undefined>|undefined} A promise that resolves to the tile content once loaded; a promise that resolves to undefined if the tile was destroyed before processing can happen or the request was cancelled mid-flight; or undefined if the request cannot be scheduled this frame.
  1182. */
  1183. function requestSingleContent(tile) {
  1184. // it is important to clone here. The fetchArrayBuffer() below uses
  1185. // throttling, but other uses of the resources do not.
  1186. const resource = tile._contentResource.clone();
  1187. const expired = tile.contentExpired;
  1188. if (expired) {
  1189. // Append a query parameter of the tile expiration date to prevent caching
  1190. resource.setQueryParameters({
  1191. expired: tile.expireDate.toString(),
  1192. });
  1193. }
  1194. const request = new Request({
  1195. throttle: true,
  1196. throttleByServer: true,
  1197. type: RequestType.TILES3D,
  1198. priorityFunction: createPriorityFunction(tile),
  1199. serverKey: tile._serverKey,
  1200. });
  1201. tile._request = request;
  1202. resource.request = request;
  1203. const tileset = tile._tileset;
  1204. const promise = resource.fetchArrayBuffer();
  1205. if (!defined(promise)) {
  1206. ++tileset.statistics.numberOfAttemptedRequests;
  1207. return;
  1208. }
  1209. return processArrayBuffer(tile, tileset, request, expired, promise);
  1210. }
  1211. /**
  1212. * Determines whether a tile load error should be interpreted as "no content"
  1213. * (empty tile) rather than a true failure. A missing tile policy specifies
  1214. * HTTP Status Codes to be interpreted as "no content", allowing tiles to be
  1215. * statically hosted without generating and serving unnecessary content for
  1216. * empty tiles.
  1217. * @ignore
  1218. */
  1219. function isEmptyTile(tile, error) {
  1220. const tileset = tile._tileset;
  1221. const policy = tileset?._runtimeContentCodec?.missingTilePolicy;
  1222. if (!defined(policy)) {
  1223. return false;
  1224. }
  1225. if (!defined(error.statusCode)) {
  1226. return false;
  1227. }
  1228. return policy.statusCodes.includes(error.statusCode);
  1229. }
  1230. function markTileAsEmptyContent(tile) {
  1231. // These flags are mutable instance state initialized in the constructor.
  1232. tile.hasEmptyContent = true;
  1233. tile.hasRenderableContent = false;
  1234. }
  1235. Cesium3DTile._isEmptyTile = isEmptyTile;
  1236. /**
  1237. * Given a downloaded content payload, construct a {@link Cesium3DTileContent}.
  1238. * <p>
  1239. * This is only used for single contents.
  1240. * </p>
  1241. *
  1242. * @param {Cesium3DTile} tile The tile
  1243. * @param {ArrayBuffer} arrayBuffer The downloaded payload containing data for the content
  1244. * @return {Promise<Cesium3DTileContent>} A content object
  1245. * @private
  1246. */
  1247. async function makeContent(tile, arrayBuffer) {
  1248. const tileset = tile._tileset;
  1249. const codec = tileset?._runtimeContentCodec;
  1250. if (defined(codec) && typeof codec.createContent === "function") {
  1251. const content = await Promise.resolve(
  1252. codec.createContent(tileset, tile, tile._contentResource, arrayBuffer),
  1253. );
  1254. if (tile.isDestroyed()) {
  1255. return;
  1256. }
  1257. return content;
  1258. }
  1259. const preprocessed = preprocess3DTileContent(arrayBuffer);
  1260. // Vector and Geometry tile rendering do not support the skip LOD optimization.
  1261. tileset._disableSkipLevelOfDetail =
  1262. tileset._disableSkipLevelOfDetail ||
  1263. preprocessed.contentType === Cesium3DTileContentType.GEOMETRY ||
  1264. preprocessed.contentType === Cesium3DTileContentType.VECTOR;
  1265. if (
  1266. preprocessed.contentType === Cesium3DTileContentType.IMPLICIT_SUBTREE ||
  1267. preprocessed.contentType === Cesium3DTileContentType.IMPLICIT_SUBTREE_JSON
  1268. ) {
  1269. tile.hasImplicitContent = true;
  1270. tile.hasRenderableContent = false;
  1271. }
  1272. if (preprocessed.contentType === Cesium3DTileContentType.EXTERNAL_TILESET) {
  1273. tile.hasTilesetContent = true;
  1274. tile.hasRenderableContent = false;
  1275. }
  1276. let content;
  1277. const contentFactory = Cesium3DTileContentFactory[preprocessed.contentType];
  1278. if (tile.isDestroyed()) {
  1279. return;
  1280. }
  1281. if (defined(preprocessed.binaryPayload)) {
  1282. content = await Promise.resolve(
  1283. contentFactory(
  1284. tileset,
  1285. tile,
  1286. tile._contentResource,
  1287. preprocessed.binaryPayload.buffer,
  1288. 0,
  1289. ),
  1290. );
  1291. } else {
  1292. // JSON formats
  1293. content = await Promise.resolve(
  1294. contentFactory(
  1295. tileset,
  1296. tile,
  1297. tile._contentResource,
  1298. preprocessed.jsonPayload,
  1299. ),
  1300. );
  1301. }
  1302. const contentHeader = tile._contentHeader;
  1303. if (tile.hasImplicitContentMetadata) {
  1304. const subtree = tile.implicitSubtree;
  1305. const coordinates = tile.implicitCoordinates;
  1306. content.metadata = subtree.getContentMetadataView(coordinates, 0);
  1307. } else if (!tile.hasImplicitContent) {
  1308. content.metadata = findContentMetadata(tileset, contentHeader);
  1309. }
  1310. const groupMetadata = findGroupMetadata(tileset, contentHeader);
  1311. if (defined(groupMetadata)) {
  1312. content.group = new Cesium3DContentGroup({
  1313. metadata: groupMetadata,
  1314. });
  1315. }
  1316. return content;
  1317. }
  1318. /**
  1319. * Cancel requests for the tile's contents. This is called when the tile
  1320. * goes out of view.
  1321. *
  1322. * @private
  1323. */
  1324. Cesium3DTile.prototype.cancelRequests = function () {
  1325. if (this.hasMultipleContents) {
  1326. this._content.cancelRequests();
  1327. } else {
  1328. this._request.cancel();
  1329. }
  1330. };
  1331. /**
  1332. * Unloads the tile's content.
  1333. *
  1334. * @private
  1335. */
  1336. Cesium3DTile.prototype.unloadContent = function () {
  1337. if (!this.hasRenderableContent) {
  1338. return;
  1339. }
  1340. this._content = this._content && this._content.destroy();
  1341. this._contentState = Cesium3DTileContentState.UNLOADED;
  1342. this.lastStyleTime = 0.0;
  1343. this.clippingPlanesDirty = this._clippingPlanesState === 0;
  1344. this._clippingPlanesState = 0;
  1345. this.clippingPolygonsDirty = this._clippingPolygonsState === 0;
  1346. this._clippingPolygonsState = 0;
  1347. this._debugColorizeTiles = false;
  1348. this._debugBoundingVolume =
  1349. this._debugBoundingVolume && this._debugBoundingVolume.destroy();
  1350. this._debugContentBoundingVolume =
  1351. this._debugContentBoundingVolume &&
  1352. this._debugContentBoundingVolume.destroy();
  1353. this._debugViewerRequestVolume =
  1354. this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy();
  1355. };
  1356. const scratchProjectedBoundingSphere = new BoundingSphere();
  1357. /**
  1358. * @private
  1359. * @param {Cesium3DTile} tile
  1360. * @param {FrameState} frameState
  1361. * @returns {TileBoundingVolume}
  1362. */
  1363. function getBoundingVolume(tile, frameState) {
  1364. if (
  1365. frameState.mode !== SceneMode.SCENE3D &&
  1366. !defined(tile._boundingVolume2D)
  1367. ) {
  1368. const boundingSphere = tile._boundingVolume.boundingSphere;
  1369. const sphere = BoundingSphere.projectTo2D(
  1370. boundingSphere,
  1371. frameState.mapProjection,
  1372. scratchProjectedBoundingSphere,
  1373. );
  1374. tile._boundingVolume2D = new TileBoundingSphere(
  1375. sphere.center,
  1376. sphere.radius,
  1377. );
  1378. }
  1379. return frameState.mode !== SceneMode.SCENE3D
  1380. ? tile._boundingVolume2D
  1381. : tile._boundingVolume;
  1382. }
  1383. /**
  1384. * @private
  1385. * @param {Cesium3DTile} tile
  1386. * @param {FrameState} frameState
  1387. * @returns {TileBoundingVolume}
  1388. */
  1389. function getContentBoundingVolume(tile, frameState) {
  1390. if (
  1391. frameState.mode !== SceneMode.SCENE3D &&
  1392. !defined(tile._contentBoundingVolume2D)
  1393. ) {
  1394. const boundingSphere = tile._contentBoundingVolume.boundingSphere;
  1395. const sphere = BoundingSphere.projectTo2D(
  1396. boundingSphere,
  1397. frameState.mapProjection,
  1398. scratchProjectedBoundingSphere,
  1399. );
  1400. tile._contentBoundingVolume2D = new TileBoundingSphere(
  1401. sphere.center,
  1402. sphere.radius,
  1403. );
  1404. }
  1405. return frameState.mode !== SceneMode.SCENE3D
  1406. ? tile._contentBoundingVolume2D
  1407. : tile._contentBoundingVolume;
  1408. }
  1409. /**
  1410. * Determines whether the tile's bounding volume intersects the culling volume.
  1411. *
  1412. * @param {FrameState} frameState The frame state.
  1413. * @param {number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check.
  1414. * @returns {number} A plane mask as described above in {@link CullingVolume#computeVisibilityWithPlaneMask}.
  1415. *
  1416. * @private
  1417. */
  1418. Cesium3DTile.prototype.visibility = function (
  1419. frameState,
  1420. parentVisibilityPlaneMask,
  1421. ) {
  1422. const cullingVolume = frameState.cullingVolume;
  1423. const boundingVolume = getBoundingVolume(this, frameState);
  1424. const tileset = this._tileset;
  1425. const clippingPlanes = tileset.clippingPlanes;
  1426. if (defined(clippingPlanes) && clippingPlanes.enabled) {
  1427. const intersection = clippingPlanes.computeIntersectionWithBoundingVolume(
  1428. boundingVolume,
  1429. tileset.clippingPlanesOriginMatrix,
  1430. );
  1431. this._isClipped = intersection !== Intersect.INSIDE;
  1432. if (intersection === Intersect.OUTSIDE) {
  1433. return CullingVolume.MASK_OUTSIDE;
  1434. }
  1435. }
  1436. const clippingPolygons = tileset.clippingPolygons;
  1437. if (defined(clippingPolygons) && clippingPolygons.enabled) {
  1438. const intersection =
  1439. clippingPolygons.computeIntersectionWithBoundingVolume(boundingVolume);
  1440. this._isClippedByPolygon = intersection !== Intersect.OUTSIDE;
  1441. // Polygon clipping intersections are determined by outer rectangles, therefore we cannot
  1442. // preemptively determine if a tile is completely clipped or not here.
  1443. }
  1444. return cullingVolume.computeVisibilityWithPlaneMask(
  1445. boundingVolume,
  1446. parentVisibilityPlaneMask,
  1447. );
  1448. };
  1449. /**
  1450. * Assuming the tile's bounding volume intersects the culling volume, determines
  1451. * whether the tile's content's bounding volume intersects the culling volume.
  1452. *
  1453. * @param {FrameState} frameState The frame state.
  1454. * @returns {Intersect} The result of the intersection: the tile's content is completely outside, completely inside, or intersecting the culling volume.
  1455. *
  1456. * @private
  1457. */
  1458. Cesium3DTile.prototype.contentVisibility = function (frameState) {
  1459. // Assumes the tile's bounding volume intersects the culling volume already, so
  1460. // just return Intersect.INSIDE if there is no content bounding volume.
  1461. if (!defined(this._contentBoundingVolume)) {
  1462. return Intersect.INSIDE;
  1463. }
  1464. if (this._visibilityPlaneMask === CullingVolume.MASK_INSIDE) {
  1465. // The tile's bounding volume is completely inside the culling volume so
  1466. // the content bounding volume must also be inside.
  1467. return Intersect.INSIDE;
  1468. }
  1469. // PERFORMANCE_IDEA: is it possible to burn less CPU on this test since we know the
  1470. // tile's (not the content's) bounding volume intersects the culling volume?
  1471. const cullingVolume = frameState.cullingVolume;
  1472. const boundingVolume = getContentBoundingVolume(this, frameState);
  1473. const tileset = this._tileset;
  1474. const clippingPlanes = tileset.clippingPlanes;
  1475. if (defined(clippingPlanes) && clippingPlanes.enabled) {
  1476. const intersection = clippingPlanes.computeIntersectionWithBoundingVolume(
  1477. boundingVolume,
  1478. tileset.clippingPlanesOriginMatrix,
  1479. );
  1480. this._isClipped = intersection !== Intersect.INSIDE;
  1481. if (intersection === Intersect.OUTSIDE) {
  1482. return Intersect.OUTSIDE;
  1483. }
  1484. }
  1485. const clippingPolygons = tileset.clippingPolygons;
  1486. if (defined(clippingPolygons) && clippingPolygons.enabled) {
  1487. const intersection =
  1488. clippingPolygons.computeIntersectionWithBoundingVolume(boundingVolume);
  1489. this._isClippedByPolygon = intersection !== Intersect.OUTSIDE;
  1490. if (intersection === Intersect.INSIDE) {
  1491. return Intersect.OUTSIDE;
  1492. }
  1493. }
  1494. return cullingVolume.computeVisibility(boundingVolume);
  1495. };
  1496. /**
  1497. * Computes the (potentially approximate) distance from the closest point of the tile's bounding volume to the camera.
  1498. *
  1499. * @param {FrameState} frameState The frame state.
  1500. * @returns {number} The distance, in meters, or zero if the camera is inside the bounding volume.
  1501. *
  1502. * @private
  1503. */
  1504. Cesium3DTile.prototype.distanceToTile = function (frameState) {
  1505. const boundingVolume = getBoundingVolume(this, frameState);
  1506. return boundingVolume.distanceToCamera(frameState);
  1507. };
  1508. const scratchToTileCenter = new Cartesian3();
  1509. /**
  1510. * Computes the distance from the center of the tile's bounding volume to the camera's plane defined by its position and view direction.
  1511. *
  1512. * @param {FrameState} frameState The frame state.
  1513. * @returns {number} The distance, in meters.
  1514. *
  1515. * @private
  1516. */
  1517. Cesium3DTile.prototype.distanceToTileCenter = function (frameState) {
  1518. const tileBoundingVolume = getBoundingVolume(this, frameState);
  1519. const boundingVolume = tileBoundingVolume.boundingVolume; // Gets the underlying OrientedBoundingBox or BoundingSphere
  1520. const toCenter = Cartesian3.subtract(
  1521. boundingVolume.center,
  1522. frameState.camera.positionWC,
  1523. scratchToTileCenter,
  1524. );
  1525. return Cartesian3.dot(frameState.camera.directionWC, toCenter);
  1526. };
  1527. /**
  1528. * Checks if the camera is inside the viewer request volume.
  1529. *
  1530. * @param {FrameState} frameState The frame state.
  1531. * @returns {boolean} Whether the camera is inside the volume.
  1532. *
  1533. * @private
  1534. */
  1535. Cesium3DTile.prototype.insideViewerRequestVolume = function (frameState) {
  1536. const viewerRequestVolume = this._viewerRequestVolume;
  1537. return (
  1538. !defined(viewerRequestVolume) ||
  1539. viewerRequestVolume.distanceToCamera(frameState) === 0.0
  1540. );
  1541. };
  1542. const scratchMatrix = new Matrix3();
  1543. const scratchScale = new Cartesian3();
  1544. const scratchHalfAxes = new Matrix3();
  1545. const scratchCenter = new Cartesian3();
  1546. const scratchRectangle = new Rectangle();
  1547. const scratchOrientedBoundingBox = new OrientedBoundingBox();
  1548. const scratchTransform = new Matrix4();
  1549. /**
  1550. * @private
  1551. * @param {Array} box An array of 12 numbers that define an oriented bounding box
  1552. * @param {Matrix4} transform
  1553. * @param {TileBoundingVolume} [result]
  1554. * @returns {TileOrientedBoundingBox}
  1555. */
  1556. function createBox(box, transform, result) {
  1557. let center = Cartesian3.fromElements(box[0], box[1], box[2], scratchCenter);
  1558. let halfAxes = Matrix3.fromArray(box, 3, scratchHalfAxes);
  1559. // Find the transformed center and halfAxes
  1560. center = Matrix4.multiplyByPoint(transform, center, center);
  1561. const rotationScale = Matrix4.getMatrix3(transform, scratchMatrix);
  1562. halfAxes = Matrix3.multiply(rotationScale, halfAxes, halfAxes);
  1563. if (defined(result)) {
  1564. result.update(center, halfAxes);
  1565. return result;
  1566. }
  1567. return new TileOrientedBoundingBox(center, halfAxes);
  1568. }
  1569. /**
  1570. * @private
  1571. * @param {Array} region An array of six numbers that define a bounding geographic region in EPSG:4979 coordinates with the order [west, south, east, north, minimum height, maximum height]
  1572. * @param {Matrix4} transform
  1573. * @param {Matrix4} initialTransform
  1574. * @param {TileOrientedBoundingBox} [result]
  1575. * @returns {TileOrientedBoundingBox}
  1576. */
  1577. function createBoxFromTransformedRegion(
  1578. region,
  1579. transform,
  1580. initialTransform,
  1581. result,
  1582. ) {
  1583. const rectangle = Rectangle.unpack(region, 0, scratchRectangle);
  1584. const minimumHeight = region[4];
  1585. const maximumHeight = region[5];
  1586. const orientedBoundingBox = OrientedBoundingBox.fromRectangle(
  1587. rectangle,
  1588. minimumHeight,
  1589. maximumHeight,
  1590. Ellipsoid.WGS84,
  1591. scratchOrientedBoundingBox,
  1592. );
  1593. let center = orientedBoundingBox.center;
  1594. let halfAxes = orientedBoundingBox.halfAxes;
  1595. // A region bounding volume is not transformed by the transform in the tileset JSON,
  1596. // but may be transformed by additional transforms applied in Cesium.
  1597. // This is why the transform is calculated as the difference between the initial transform and the current transform.
  1598. transform = Matrix4.multiplyTransformation(
  1599. transform,
  1600. Matrix4.inverseTransformation(initialTransform, scratchTransform),
  1601. scratchTransform,
  1602. );
  1603. center = Matrix4.multiplyByPoint(transform, center, center);
  1604. const rotationScale = Matrix4.getMatrix3(transform, scratchMatrix);
  1605. halfAxes = Matrix3.multiply(rotationScale, halfAxes, halfAxes);
  1606. if (defined(result) && result instanceof TileOrientedBoundingBox) {
  1607. result.update(center, halfAxes);
  1608. return result;
  1609. }
  1610. return new TileOrientedBoundingBox(center, halfAxes);
  1611. }
  1612. /**
  1613. * Creates a TileBoundingVolume from the given region and transform
  1614. * information.
  1615. *
  1616. * This may either be a TileBoundingRegion or a TileOrientedBoundingBox.
  1617. *
  1618. * If the given transform is the initial transform, then this will return
  1619. * a TileBoundingRegion. This will either be the given result parameter,
  1620. * or a new TileBoundingRegion, if the given result parameter was not
  1621. * a TileBoundingRegion.
  1622. *
  1623. * If the given transform deviates from the initial transform, then this
  1624. * will return a TileOrientedBoundingBox that was computed by applying
  1625. * the given transform to the given region. This will either be the
  1626. * given result parameter, or a new TileOrientedBoundingBox, if the given
  1627. * result parameter was not a TileOrientedBoundingBox
  1628. *
  1629. * @private
  1630. * @param {Array} region An array of six numbers that define a bounding
  1631. * geographic region in EPSG:4979 coordinates with the order
  1632. * [west, south, east, north, minimum height, maximum height]
  1633. * @param {Matrix4} transform The current computedTransform of the tile,
  1634. * which includes the parent transform (which, in turn, includes the
  1635. * modelMatrix of the containing tileset)
  1636. * @param {Matrix4} initialTransform The initial transform of the tile,
  1637. * before any changes to the modelMatrix of the containing tileset
  1638. * @param {TileBoundingVolume} [result] An optional result.
  1639. * @returns {TileBoundingVolume} The resulting bounding volume
  1640. */
  1641. function createRegion(region, transform, initialTransform, result) {
  1642. if (
  1643. !Matrix4.equalsEpsilon(transform, initialTransform, CesiumMath.EPSILON8)
  1644. ) {
  1645. if (result instanceof TileOrientedBoundingBox) {
  1646. return createBoxFromTransformedRegion(
  1647. region,
  1648. transform,
  1649. initialTransform,
  1650. result,
  1651. );
  1652. }
  1653. return createBoxFromTransformedRegion(
  1654. region,
  1655. transform,
  1656. initialTransform,
  1657. undefined,
  1658. );
  1659. }
  1660. const rectangleRegion = Rectangle.unpack(region, 0, scratchRectangle);
  1661. if (result instanceof TileBoundingRegion) {
  1662. result.rectangle = Rectangle.clone(rectangleRegion, result.rectangle);
  1663. result.minimumHeight = region[4];
  1664. result.maximumHeight = region[5];
  1665. // The TileBoundingRegion was already constructed with the default
  1666. // WGS84 ellipsoid, so keep it consistent when updating.
  1667. result.computeBoundingVolumes(Ellipsoid.WGS84);
  1668. return result;
  1669. }
  1670. return new TileBoundingRegion({
  1671. rectangle: rectangleRegion,
  1672. minimumHeight: region[4],
  1673. maximumHeight: region[5],
  1674. });
  1675. }
  1676. /**
  1677. * @private
  1678. * @param {Array} sphere An array of four numbers that define a bounding sphere
  1679. * @param {Matrix4} transform
  1680. * @param {TileBoundingVolume} [result]
  1681. * @returns {TileBoundingSphere}
  1682. */
  1683. function createSphere(sphere, transform, result) {
  1684. let center = Cartesian3.fromElements(
  1685. sphere[0],
  1686. sphere[1],
  1687. sphere[2],
  1688. scratchCenter,
  1689. );
  1690. let radius = sphere[3];
  1691. // Find the transformed center and radius
  1692. center = Matrix4.multiplyByPoint(transform, center, center);
  1693. const scale = Matrix4.getScale(transform, scratchScale);
  1694. const uniformScale = Cartesian3.maximumComponent(scale);
  1695. radius *= uniformScale;
  1696. if (defined(result)) {
  1697. result.update(center, radius);
  1698. return result;
  1699. }
  1700. return new TileBoundingSphere(center, radius);
  1701. }
  1702. /**
  1703. * Create a bounding volume from the tile's bounding volume header.
  1704. *
  1705. * @param {object} boundingVolumeHeader The tile's bounding volume header.
  1706. * @param {Matrix4} transform The transform to apply to the bounding volume.
  1707. * @param {TileBoundingVolume} [result] The object onto which to store the result.
  1708. *
  1709. * @returns {TileBoundingVolume} The modified result parameter or a new TileBoundingVolume instance if none was provided.
  1710. *
  1711. * @private
  1712. */
  1713. Cesium3DTile.prototype.createBoundingVolume = function (
  1714. boundingVolumeHeader,
  1715. transform,
  1716. result,
  1717. ) {
  1718. // if explicit tile metadata includes TILE_BOUNDING_BOX, TILE_BOUNDING_REGION,
  1719. // or TILE_BOUNDING_SPHERE, override tile.boundingVolume.
  1720. const tileMetadata = this.metadata;
  1721. let metadataBoundingVolumeHeader;
  1722. if (defined(tileMetadata)) {
  1723. metadataBoundingVolumeHeader =
  1724. BoundingVolumeSemantics.parseBoundingVolumeSemantic("TILE", tileMetadata);
  1725. }
  1726. if (defined(metadataBoundingVolumeHeader)) {
  1727. boundingVolumeHeader = metadataBoundingVolumeHeader;
  1728. }
  1729. if (!defined(boundingVolumeHeader)) {
  1730. throw new RuntimeError("boundingVolume must be defined");
  1731. }
  1732. if (hasExtension(boundingVolumeHeader, "3DTILES_bounding_volume_S2")) {
  1733. return new TileBoundingS2Cell(
  1734. boundingVolumeHeader.extensions["3DTILES_bounding_volume_S2"],
  1735. );
  1736. }
  1737. const { box, region, sphere } = boundingVolumeHeader;
  1738. if (defined(box)) {
  1739. const tileOrientedBoundingBox = createBox(box, transform, result);
  1740. if (this._verticalExaggeration !== 1.0) {
  1741. exaggerateBoundingBox(
  1742. tileOrientedBoundingBox,
  1743. this._verticalExaggeration,
  1744. this._verticalExaggerationRelativeHeight,
  1745. );
  1746. }
  1747. return tileOrientedBoundingBox;
  1748. }
  1749. if (defined(region)) {
  1750. const tileBoundingVolume = createRegion(
  1751. region,
  1752. transform,
  1753. this._initialTransform,
  1754. result,
  1755. );
  1756. if (this._verticalExaggeration === 1.0) {
  1757. return tileBoundingVolume;
  1758. }
  1759. if (tileBoundingVolume instanceof TileOrientedBoundingBox) {
  1760. exaggerateBoundingBox(
  1761. tileBoundingVolume,
  1762. this._verticalExaggeration,
  1763. this._verticalExaggerationRelativeHeight,
  1764. );
  1765. } else {
  1766. tileBoundingVolume.minimumHeight = VerticalExaggeration.getHeight(
  1767. tileBoundingVolume.minimumHeight,
  1768. this._verticalExaggeration,
  1769. this._verticalExaggerationRelativeHeight,
  1770. );
  1771. tileBoundingVolume.maximumHeight = VerticalExaggeration.getHeight(
  1772. tileBoundingVolume.maximumHeight,
  1773. this._verticalExaggeration,
  1774. this._verticalExaggerationRelativeHeight,
  1775. );
  1776. tileBoundingVolume.computeBoundingVolumes(Ellipsoid.WGS84);
  1777. }
  1778. return tileBoundingVolume;
  1779. }
  1780. if (defined(sphere)) {
  1781. const tileBoundingSphere = createSphere(sphere, transform, result);
  1782. if (this._verticalExaggeration !== 1.0) {
  1783. const exaggeratedCenter = VerticalExaggeration.getPosition(
  1784. tileBoundingSphere.center,
  1785. Ellipsoid.WGS84,
  1786. this._verticalExaggeration,
  1787. this._verticalExaggerationRelativeHeight,
  1788. scratchCenter,
  1789. );
  1790. const exaggeratedRadius =
  1791. tileBoundingSphere.radius * this._verticalExaggeration;
  1792. tileBoundingSphere.update(exaggeratedCenter, exaggeratedRadius);
  1793. }
  1794. return tileBoundingSphere;
  1795. }
  1796. throw new RuntimeError(
  1797. "boundingVolume must contain a sphere, region, or box",
  1798. );
  1799. };
  1800. const scratchExaggeratedCorners = Cartesian3.unpackArray(
  1801. new Array(8 * 3).fill(0),
  1802. );
  1803. /**
  1804. * Exaggerates the bounding box of a tile based on the provided exaggeration factors.
  1805. *
  1806. * @private
  1807. * @param {TileOrientedBoundingBox} tileOrientedBoundingBox - The oriented bounding box of the tile.
  1808. * @param {number} exaggeration - The exaggeration factor to apply to the tile's bounding box.
  1809. * @param {number} exaggerationRelativeHeight - The height relative to which exaggeration will be applied.
  1810. */
  1811. function exaggerateBoundingBox(
  1812. tileOrientedBoundingBox,
  1813. exaggeration,
  1814. exaggerationRelativeHeight,
  1815. ) {
  1816. const exaggeratedCorners = tileOrientedBoundingBox.boundingVolume
  1817. .computeCorners(scratchExaggeratedCorners)
  1818. .map((corner) =>
  1819. VerticalExaggeration.getPosition(
  1820. corner,
  1821. Ellipsoid.WGS84,
  1822. exaggeration,
  1823. exaggerationRelativeHeight,
  1824. corner,
  1825. ),
  1826. );
  1827. const exaggeratedBox = OrientedBoundingBox.fromPoints(
  1828. exaggeratedCorners,
  1829. scratchOrientedBoundingBox,
  1830. );
  1831. tileOrientedBoundingBox.update(
  1832. exaggeratedBox.center,
  1833. exaggeratedBox.halfAxes,
  1834. );
  1835. }
  1836. /**
  1837. * Update the tile's transform. The transform is applied to the tile's bounding volumes.
  1838. *
  1839. * @private
  1840. * @param {Matrix4} parentTransform
  1841. * @param {FrameState} [frameState]
  1842. */
  1843. Cesium3DTile.prototype.updateTransform = function (
  1844. parentTransform,
  1845. frameState,
  1846. ) {
  1847. parentTransform = parentTransform ?? Matrix4.IDENTITY;
  1848. const computedTransform = Matrix4.multiplyTransformation(
  1849. parentTransform,
  1850. this.transform,
  1851. scratchTransform,
  1852. );
  1853. const transformChanged = !Matrix4.equals(
  1854. computedTransform,
  1855. this.computedTransform,
  1856. );
  1857. const exaggerationChanged =
  1858. defined(frameState) &&
  1859. (this._verticalExaggeration !== frameState.verticalExaggeration ||
  1860. this._verticalExaggerationRelativeHeight !==
  1861. frameState.verticalExaggerationRelativeHeight);
  1862. if (!transformChanged && !exaggerationChanged) {
  1863. return;
  1864. }
  1865. if (transformChanged) {
  1866. Matrix4.clone(computedTransform, this.computedTransform);
  1867. }
  1868. if (exaggerationChanged) {
  1869. this._verticalExaggeration = frameState.verticalExaggeration;
  1870. this._verticalExaggerationRelativeHeight =
  1871. frameState.verticalExaggerationRelativeHeight;
  1872. }
  1873. // Update the bounding volumes
  1874. const header = this._header;
  1875. const contentHeader = this._contentHeader;
  1876. this._boundingVolume = this.createBoundingVolume(
  1877. header.boundingVolume,
  1878. this.computedTransform,
  1879. this._boundingVolume,
  1880. );
  1881. if (defined(this._contentBoundingVolume)) {
  1882. this._contentBoundingVolume = this.createBoundingVolume(
  1883. contentHeader.boundingVolume,
  1884. this.computedTransform,
  1885. this._contentBoundingVolume,
  1886. );
  1887. }
  1888. if (defined(this._viewerRequestVolume)) {
  1889. this._viewerRequestVolume = this.createBoundingVolume(
  1890. header.viewerRequestVolume,
  1891. this.computedTransform,
  1892. this._viewerRequestVolume,
  1893. );
  1894. }
  1895. this.updateGeometricErrorScale();
  1896. // Destroy the debug bounding volumes. They will be generated fresh.
  1897. this._debugBoundingVolume =
  1898. this._debugBoundingVolume && this._debugBoundingVolume.destroy();
  1899. this._debugContentBoundingVolume =
  1900. this._debugContentBoundingVolume &&
  1901. this._debugContentBoundingVolume.destroy();
  1902. this._debugViewerRequestVolume =
  1903. this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy();
  1904. };
  1905. Cesium3DTile.prototype.updateGeometricErrorScale = function () {
  1906. const scale = Matrix4.getScale(this.computedTransform, scratchScale);
  1907. const uniformScale = Cartesian3.maximumComponent(scale);
  1908. this.geometricError = this._geometricError * uniformScale;
  1909. if (!defined(this.parent)) {
  1910. // Update the tileset's geometric error
  1911. const tileset = this._tileset;
  1912. tileset._scaledGeometricError = tileset._geometricError * uniformScale;
  1913. }
  1914. };
  1915. /**
  1916. * @private
  1917. * @param {Cesium3DTile} tile
  1918. * @param {Cesium3DTileset} tileset
  1919. * @param {FrameState} frameState
  1920. * @param {object} passOptions
  1921. */
  1922. function applyDebugSettings(tile, tileset, frameState, passOptions) {
  1923. if (!passOptions.isRender) {
  1924. return;
  1925. }
  1926. const hasContentBoundingVolume =
  1927. defined(tile._contentHeader) && defined(tile._contentHeader.boundingVolume);
  1928. const showVolume =
  1929. tileset.debugShowBoundingVolume ||
  1930. (tileset.debugShowContentBoundingVolume && !hasContentBoundingVolume);
  1931. if (showVolume) {
  1932. let color;
  1933. if (!tile._finalResolution) {
  1934. color = Color.YELLOW;
  1935. } else if (!tile.hasRenderableContent) {
  1936. color = Color.DARKGRAY;
  1937. } else {
  1938. color = Color.WHITE;
  1939. }
  1940. if (!defined(tile._debugBoundingVolume)) {
  1941. tile._debugBoundingVolume = tile._boundingVolume.createDebugVolume(color);
  1942. }
  1943. tile._debugBoundingVolume.update(frameState);
  1944. const attributes =
  1945. tile._debugBoundingVolume.getGeometryInstanceAttributes("outline");
  1946. attributes.color = ColorGeometryInstanceAttribute.toValue(
  1947. color,
  1948. attributes.color,
  1949. );
  1950. } else if (!showVolume && defined(tile._debugBoundingVolume)) {
  1951. tile._debugBoundingVolume = tile._debugBoundingVolume.destroy();
  1952. }
  1953. if (tileset.debugShowContentBoundingVolume && hasContentBoundingVolume) {
  1954. if (!defined(tile._debugContentBoundingVolume)) {
  1955. tile._debugContentBoundingVolume =
  1956. tile._contentBoundingVolume.createDebugVolume(Color.BLUE);
  1957. }
  1958. tile._debugContentBoundingVolume.update(frameState);
  1959. } else if (
  1960. !tileset.debugShowContentBoundingVolume &&
  1961. defined(tile._debugContentBoundingVolume)
  1962. ) {
  1963. tile._debugContentBoundingVolume =
  1964. tile._debugContentBoundingVolume.destroy();
  1965. }
  1966. if (
  1967. tileset.debugShowViewerRequestVolume &&
  1968. defined(tile._viewerRequestVolume)
  1969. ) {
  1970. if (!defined(tile._debugViewerRequestVolume)) {
  1971. tile._debugViewerRequestVolume =
  1972. tile._viewerRequestVolume.createDebugVolume(Color.YELLOW);
  1973. }
  1974. tile._debugViewerRequestVolume.update(frameState);
  1975. } else if (
  1976. !tileset.debugShowViewerRequestVolume &&
  1977. defined(tile._debugViewerRequestVolume)
  1978. ) {
  1979. tile._debugViewerRequestVolume = tile._debugViewerRequestVolume.destroy();
  1980. }
  1981. const debugColorizeTilesOn =
  1982. (tileset.debugColorizeTiles && !tile._debugColorizeTiles) ||
  1983. defined(tileset._heatmap.tilePropertyName);
  1984. const debugColorizeTilesOff =
  1985. !tileset.debugColorizeTiles && tile._debugColorizeTiles;
  1986. if (debugColorizeTilesOn) {
  1987. tileset._heatmap.colorize(tile, frameState); // Skipped if tileset._heatmap.tilePropertyName is undefined
  1988. tile._debugColorizeTiles = true;
  1989. tile.color = tile._debugColor;
  1990. } else if (debugColorizeTilesOff) {
  1991. tile._debugColorizeTiles = false;
  1992. tile.color = Color.WHITE;
  1993. }
  1994. if (tile._colorDirty) {
  1995. tile._colorDirty = false;
  1996. tile._content.applyDebugSettings(true, tile._color);
  1997. }
  1998. if (debugColorizeTilesOff) {
  1999. tileset.makeStyleDirty(); // Re-apply style now that colorize is switched off
  2000. }
  2001. }
  2002. /**
  2003. * @private
  2004. * @param {Cesium3DTile} tile
  2005. * @param {Cesium3DTileset} tileset
  2006. * @param {FrameState} frameState
  2007. */
  2008. function updateContent(tile, tileset, frameState) {
  2009. const expiredContent = tile._expiredContent;
  2010. // expired content is not supported for multiple contents
  2011. if (!tile.hasMultipleContents && defined(expiredContent)) {
  2012. if (!tile.contentReady) {
  2013. // Render the expired content while the content loads
  2014. try {
  2015. expiredContent.update(tileset, frameState);
  2016. } catch (error) {
  2017. // Eat error for expired content
  2018. }
  2019. return;
  2020. }
  2021. // New content is ready, destroy expired content
  2022. tile._expiredContent.destroy();
  2023. tile._expiredContent = undefined;
  2024. }
  2025. if (!defined(tile.content)) {
  2026. // Implicit placeholder tile
  2027. return;
  2028. }
  2029. try {
  2030. tile.content.update(tileset, frameState);
  2031. } catch (error) {
  2032. tile._contentState = Cesium3DTileContentState.FAILED;
  2033. throw error;
  2034. }
  2035. }
  2036. /**
  2037. * Compute and compare ClippingPlanes state:
  2038. * - enabled-ness - are clipping planes enabled? is this tile clipped?
  2039. * - clipping plane count
  2040. * - clipping function (union v. intersection)
  2041. * @private
  2042. * @param {Cesium3DTile} tile
  2043. * @param {Cesium3DTileset} tileset
  2044. */
  2045. function updateClippingPlanes(tile, tileset) {
  2046. const clippingPlanes = tileset.clippingPlanes;
  2047. let currentClippingPlanesState = 0;
  2048. if (defined(clippingPlanes) && tile._isClipped && clippingPlanes.enabled) {
  2049. currentClippingPlanesState = clippingPlanes.clippingPlanesState;
  2050. }
  2051. // If clippingPlaneState for tile changed, mark clippingPlanesDirty so content can update
  2052. if (currentClippingPlanesState !== tile._clippingPlanesState) {
  2053. tile._clippingPlanesState = currentClippingPlanesState;
  2054. tile.clippingPlanesDirty = true;
  2055. }
  2056. }
  2057. /**
  2058. * Compute and compare ClippingPolygons state:
  2059. * - enabled-ness - are clipping polygons enabled? is this tile clipped?
  2060. * - clipping polygon count & position count
  2061. * - clipping function (inverse)
  2062. * @private
  2063. * @param {Cesium3DTile} tile
  2064. * @param {Cesium3DTileset} tileset
  2065. */
  2066. function updateClippingPolygons(tile, tileset) {
  2067. const clippingPolygons = tileset.clippingPolygons;
  2068. let currentClippingPolygonsState = 0;
  2069. if (
  2070. defined(clippingPolygons) &&
  2071. tile._isClippedByPolygon &&
  2072. clippingPolygons.enabled
  2073. ) {
  2074. currentClippingPolygonsState = clippingPolygons.clippingPolygonsState;
  2075. }
  2076. // If clippingPolygonState for tile changed, mark clippingPolygonsDirty so content can update
  2077. if (currentClippingPolygonsState !== tile._clippingPolygonsState) {
  2078. tile._clippingPolygonsState = currentClippingPolygonsState;
  2079. tile.clippingPolygonsDirty = true;
  2080. }
  2081. }
  2082. /**
  2083. * Get the draw commands needed to render this tile.
  2084. *
  2085. * @private
  2086. * @param {Cesium3DTileset} tileset
  2087. * @param {FrameState} frameState
  2088. * @param {object} passOptions
  2089. */
  2090. Cesium3DTile.prototype.update = function (tileset, frameState, passOptions) {
  2091. const { commandList } = frameState;
  2092. const commandStart = commandList.length;
  2093. updateClippingPlanes(this, tileset);
  2094. updateClippingPolygons(this, tileset);
  2095. applyDebugSettings(this, tileset, frameState, passOptions);
  2096. updateContent(this, tileset, frameState);
  2097. const commandEnd = commandList.length;
  2098. this._commandsLength = commandEnd - commandStart;
  2099. for (let i = commandStart; i < commandEnd; ++i) {
  2100. const command = commandList[i];
  2101. const translucent = command.pass === Pass.TRANSLUCENT;
  2102. command.depthForTranslucentClassification = translucent;
  2103. }
  2104. this.clippingPlanesDirty = false; // reset after content update
  2105. this.clippingPolygonsDirty = false;
  2106. };
  2107. const scratchCommandList = [];
  2108. /**
  2109. * Processes the tile's content, e.g., create WebGL resources, to move from the PROCESSING to READY state.
  2110. *
  2111. * @param {Cesium3DTileset} tileset The tileset containing this tile.
  2112. * @param {FrameState} frameState The frame state.
  2113. *
  2114. * @private
  2115. */
  2116. Cesium3DTile.prototype.process = function (tileset, frameState) {
  2117. if (!this.contentExpired && !this.contentReady && this._content.ready) {
  2118. updateExpireDate(this);
  2119. // Refresh style for expired content
  2120. this._selectedFrame = 0;
  2121. this.lastStyleTime = 0.0;
  2122. JulianDate.now(this._loadTimestamp);
  2123. this._contentState = Cesium3DTileContentState.READY;
  2124. if (!this.hasTilesetContent && !this.hasImplicitContent) {
  2125. // RESEARCH_IDEA: ability to unload tiles (without content) for an
  2126. // external tileset when all the tiles are unloaded.
  2127. tileset._statistics.incrementLoadCounts(this.content);
  2128. ++tileset._statistics.numberOfTilesWithContentReady;
  2129. ++tileset._statistics.numberOfLoadedTilesTotal;
  2130. // Add to the tile cache. Previously expired tiles are already in the cache and won't get re-added.
  2131. tileset._cache.add(this);
  2132. }
  2133. }
  2134. const savedCommandList = frameState.commandList;
  2135. frameState.commandList = scratchCommandList;
  2136. try {
  2137. this._content.update(tileset, frameState);
  2138. } catch (error) {
  2139. this._contentState = Cesium3DTileContentState.FAILED;
  2140. throw error;
  2141. }
  2142. scratchCommandList.length = 0;
  2143. frameState.commandList = savedCommandList;
  2144. };
  2145. /**
  2146. * @private
  2147. * @param {number} normalizedValue
  2148. * @param {number} numberOfDigits
  2149. * @param {number} leftShift
  2150. * @returns {number}
  2151. */
  2152. function isolateDigits(normalizedValue, numberOfDigits, leftShift) {
  2153. const scaled = normalizedValue * Math.pow(10, numberOfDigits);
  2154. const integer = parseInt(scaled);
  2155. return integer * Math.pow(10, leftShift);
  2156. }
  2157. /**
  2158. * @private
  2159. * @param {number} value
  2160. * @param {number} minimum
  2161. * @param {number} maximum
  2162. * @returns {number}
  2163. */
  2164. function priorityNormalizeAndClamp(value, minimum, maximum) {
  2165. // Subtract epsilon since we only want decimal digits present in the output.
  2166. return Math.max(
  2167. CesiumMath.normalize(value, minimum, maximum) - CesiumMath.EPSILON7,
  2168. 0.0,
  2169. );
  2170. }
  2171. /**
  2172. * Sets the priority of the tile based on distance and depth
  2173. * @private
  2174. */
  2175. Cesium3DTile.prototype.updatePriority = function () {
  2176. const tileset = this.tileset;
  2177. const preferLeaves = tileset.preferLeaves;
  2178. const minimumPriority = tileset._minimumPriority;
  2179. const maximumPriority = tileset._maximumPriority;
  2180. // Combine priority systems together by mapping them into a base 10 number where each priority controls a specific set of digits in the number.
  2181. // For number priorities, map them to a 0.xxxxx number then left shift it up into a set number of digits before the decimal point. Chop of the fractional part then left shift again into the position it needs to go.
  2182. // For blending number priorities, normalize them to 0-1 and interpolate to get a combined 0-1 number, then proceed as normal.
  2183. // Booleans can just be 0 or 10^leftshift.
  2184. // Think of digits as penalties since smaller numbers are higher priority. If a tile has some large quantity or has a flag raised it's (usually) penalized for it, expressed as a higher number for the digit.
  2185. // Priority number format: preloadFlightDigits(1) | foveatedDeferDigits(1) | foveatedDigits(4) | preloadProgressiveResolutionDigits(1) | preferredSortingDigits(4) . depthDigits(the decimal digits)
  2186. // Certain flags like preferLeaves will flip / turn off certain digits to get desired load order.
  2187. // Setup leftShifts, digit counts, and scales (for booleans)
  2188. const digitsForANumber = 4;
  2189. const digitsForABoolean = 1;
  2190. const preferredSortingLeftShift = 0;
  2191. const preferredSortingDigitsCount = digitsForANumber;
  2192. const foveatedLeftShift =
  2193. preferredSortingLeftShift + preferredSortingDigitsCount;
  2194. const foveatedDigitsCount = digitsForANumber;
  2195. const preloadProgressiveResolutionLeftShift =
  2196. foveatedLeftShift + foveatedDigitsCount;
  2197. const preloadProgressiveResolutionDigitsCount = digitsForABoolean;
  2198. const preloadProgressiveResolutionScale = Math.pow(
  2199. 10,
  2200. preloadProgressiveResolutionLeftShift,
  2201. );
  2202. const foveatedDeferLeftShift =
  2203. preloadProgressiveResolutionLeftShift +
  2204. preloadProgressiveResolutionDigitsCount;
  2205. const foveatedDeferDigitsCount = digitsForABoolean;
  2206. const foveatedDeferScale = Math.pow(10, foveatedDeferLeftShift);
  2207. const preloadFlightLeftShift =
  2208. foveatedDeferLeftShift + foveatedDeferDigitsCount;
  2209. const preloadFlightScale = Math.pow(10, preloadFlightLeftShift);
  2210. // Compute the digits for each priority
  2211. let depthDigits = priorityNormalizeAndClamp(
  2212. this._depth,
  2213. minimumPriority.depth,
  2214. maximumPriority.depth,
  2215. );
  2216. depthDigits = preferLeaves ? 1.0 - depthDigits : depthDigits;
  2217. // Map 0-1 then convert to digit. Include a distance sort when doing non-skipLOD and replacement refinement, helps things like non-skipLOD photogrammetry
  2218. const useDistance =
  2219. !tileset.isSkippingLevelOfDetail &&
  2220. this.refine === Cesium3DTileRefine.REPLACE;
  2221. const normalizedPreferredSorting = useDistance
  2222. ? priorityNormalizeAndClamp(
  2223. this._priorityHolder._distanceToCamera,
  2224. minimumPriority.distance,
  2225. maximumPriority.distance,
  2226. )
  2227. : priorityNormalizeAndClamp(
  2228. this._priorityReverseScreenSpaceError,
  2229. minimumPriority.reverseScreenSpaceError,
  2230. maximumPriority.reverseScreenSpaceError,
  2231. );
  2232. const preferredSortingDigits = isolateDigits(
  2233. normalizedPreferredSorting,
  2234. preferredSortingDigitsCount,
  2235. preferredSortingLeftShift,
  2236. );
  2237. const preloadProgressiveResolutionDigits = this._priorityProgressiveResolution
  2238. ? 0
  2239. : preloadProgressiveResolutionScale;
  2240. const normalizedFoveatedFactor = priorityNormalizeAndClamp(
  2241. this._priorityHolder._foveatedFactor,
  2242. minimumPriority.foveatedFactor,
  2243. maximumPriority.foveatedFactor,
  2244. );
  2245. const foveatedDigits = isolateDigits(
  2246. normalizedFoveatedFactor,
  2247. foveatedDigitsCount,
  2248. foveatedLeftShift,
  2249. );
  2250. const foveatedDeferDigits = this.priorityDeferred ? foveatedDeferScale : 0;
  2251. const preloadFlightDigits =
  2252. tileset._pass === Cesium3DTilePass.PRELOAD_FLIGHT ? 0 : preloadFlightScale;
  2253. // Get the final base 10 number
  2254. this._priority =
  2255. depthDigits +
  2256. preferredSortingDigits +
  2257. preloadProgressiveResolutionDigits +
  2258. foveatedDigits +
  2259. foveatedDeferDigits +
  2260. preloadFlightDigits;
  2261. };
  2262. /**
  2263. * @private
  2264. */
  2265. Cesium3DTile.prototype.isDestroyed = function () {
  2266. return false;
  2267. };
  2268. /**
  2269. * @private
  2270. */
  2271. Cesium3DTile.prototype.destroy = function () {
  2272. // For the interval between new content being requested and downloaded, expiredContent === content, so don't destroy twice
  2273. this._content = this._content && this._content.destroy();
  2274. this._expiredContent =
  2275. this._expiredContent &&
  2276. !this._expiredContent.isDestroyed() &&
  2277. this._expiredContent.destroy();
  2278. this._debugBoundingVolume =
  2279. this._debugBoundingVolume && this._debugBoundingVolume.destroy();
  2280. this._debugContentBoundingVolume =
  2281. this._debugContentBoundingVolume &&
  2282. this._debugContentBoundingVolume.destroy();
  2283. this._debugViewerRequestVolume =
  2284. this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy();
  2285. return destroyObject(this);
  2286. };
  2287. export default Cesium3DTile;