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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import buildModuleUrl from "../Core/buildModuleUrl.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Cartographic from "../Core/Cartographic.js";
  5. import Color from "../Core/Color.js";
  6. import defined from "../Core/defined.js";
  7. import destroyObject from "../Core/destroyObject.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import Ellipsoid from "../Core/Ellipsoid.js";
  10. import EllipsoidTerrainProvider from "../Core/EllipsoidTerrainProvider.js";
  11. import Event from "../Core/Event.js";
  12. import IntersectionTests from "../Core/IntersectionTests.js";
  13. import NearFarScalar from "../Core/NearFarScalar.js";
  14. import Ray from "../Core/Ray.js";
  15. import Rectangle from "../Core/Rectangle.js";
  16. import Resource from "../Core/Resource.js";
  17. import ShaderSource from "../Renderer/ShaderSource.js";
  18. import Texture from "../Renderer/Texture.js";
  19. import GlobeFS from "../Shaders/GlobeFS.js";
  20. import GlobeVS from "../Shaders/GlobeVS.js";
  21. import AtmosphereCommon from "../Shaders/AtmosphereCommon.js";
  22. import GroundAtmosphere from "../Shaders/GroundAtmosphere.js";
  23. import GlobeSurfaceShaderSet from "./GlobeSurfaceShaderSet.js";
  24. import GlobeSurfaceTileProvider from "./GlobeSurfaceTileProvider.js";
  25. import GlobeTranslucency from "./GlobeTranslucency.js";
  26. import ImageryLayerCollection from "./ImageryLayerCollection.js";
  27. import QuadtreePrimitive from "./QuadtreePrimitive.js";
  28. import SceneMode from "./SceneMode.js";
  29. import ShadowMode from "./ShadowMode.js";
  30. import CesiumMath from "../Core/Math.js";
  31. /**
  32. * The globe rendered in the scene, including its terrain ({@link Globe#terrainProvider})
  33. * and imagery layers ({@link Globe#imageryLayers}). Access the globe using {@link Scene#globe}.
  34. *
  35. * @alias Globe
  36. * @constructor
  37. *
  38. * @param {Ellipsoid} [ellipsoid=Ellipsoid.default] Determines the size and shape of the
  39. * globe.
  40. */
  41. function Globe(ellipsoid) {
  42. ellipsoid = ellipsoid ?? Ellipsoid.default;
  43. const terrainProvider = new EllipsoidTerrainProvider({
  44. ellipsoid: ellipsoid,
  45. });
  46. const imageryLayerCollection = new ImageryLayerCollection();
  47. this._ellipsoid = ellipsoid;
  48. this._imageryLayerCollection = imageryLayerCollection;
  49. this._surfaceShaderSet = new GlobeSurfaceShaderSet();
  50. this._material = undefined;
  51. this._surface = new QuadtreePrimitive({
  52. tileProvider: new GlobeSurfaceTileProvider({
  53. terrainProvider: terrainProvider,
  54. imageryLayers: imageryLayerCollection,
  55. surfaceShaderSet: this._surfaceShaderSet,
  56. }),
  57. });
  58. this._terrainProvider = terrainProvider;
  59. this._terrainProviderChanged = new Event();
  60. this._undergroundColor = Color.clone(Color.BLACK);
  61. this._undergroundColorAlphaByDistance = new NearFarScalar(
  62. ellipsoid.maximumRadius / 1000.0,
  63. 0.0,
  64. ellipsoid.maximumRadius / 5.0,
  65. 1.0,
  66. );
  67. this._translucency = new GlobeTranslucency();
  68. makeShadersDirty(this);
  69. /**
  70. * Determines if the globe will be shown.
  71. *
  72. * @type {boolean}
  73. * @default true
  74. */
  75. this.show = true;
  76. this._oceanNormalMapResourceDirty = true;
  77. this._oceanNormalMapResource = new Resource({
  78. url: buildModuleUrl("Assets/Textures/waterNormalsSmall.jpg"),
  79. });
  80. /**
  81. * The maximum screen-space error used to drive level-of-detail refinement. Higher
  82. * values will provide better performance but lower visual quality.
  83. *
  84. * @type {number}
  85. * @default 2
  86. */
  87. this.maximumScreenSpaceError = 2;
  88. /**
  89. * The size of the terrain tile cache, expressed as a number of tiles. Any additional
  90. * tiles beyond this number will be freed, as long as they aren't needed for rendering
  91. * this frame. A larger number will consume more memory but will show detail faster
  92. * when, for example, zooming out and then back in.
  93. *
  94. * @type {number}
  95. * @default 100
  96. */
  97. this.tileCacheSize = 100;
  98. /**
  99. * Gets or sets the number of loading descendant tiles that is considered "too many".
  100. * If a tile has too many loading descendants, that tile will be loaded and rendered before any of
  101. * its descendants are loaded and rendered. This means more feedback for the user that something
  102. * is happening at the cost of a longer overall load time. Setting this to 0 will cause each
  103. * tile level to be loaded successively, significantly increasing load time. Setting it to a large
  104. * number (e.g. 1000) will minimize the number of tiles that are loaded but tend to make
  105. * detail appear all at once after a long wait.
  106. * @type {number}
  107. * @default 20
  108. */
  109. this.loadingDescendantLimit = 20;
  110. /**
  111. * Gets or sets a value indicating whether the ancestors of rendered tiles should be preloaded.
  112. * Setting this to true optimizes the zoom-out experience and provides more detail in
  113. * newly-exposed areas when panning. The down side is that it requires loading more tiles.
  114. * @type {boolean}
  115. * @default true
  116. */
  117. this.preloadAncestors = true;
  118. /**
  119. * Gets or sets a value indicating whether the siblings of rendered tiles should be preloaded.
  120. * Setting this to true causes tiles with the same parent as a rendered tile to be loaded, even
  121. * if they are culled. Setting this to true may provide a better panning experience at the
  122. * cost of loading more tiles.
  123. * @type {boolean}
  124. * @default false
  125. */
  126. this.preloadSiblings = false;
  127. /**
  128. * The color to use to highlight terrain fill tiles. If undefined, fill tiles are not
  129. * highlighted at all. The alpha value is used to alpha blend with the tile's
  130. * actual color. Because terrain fill tiles do not represent the actual terrain surface,
  131. * it may be useful in some applications to indicate visually that they are not to be trusted.
  132. * @type {Color}
  133. * @default undefined
  134. */
  135. this.fillHighlightColor = undefined;
  136. /**
  137. * Enable lighting the globe with the scene's light source.
  138. *
  139. * @type {boolean}
  140. * @default false
  141. */
  142. this.enableLighting = false;
  143. /**
  144. * A multiplier to adjust terrain lambert lighting.
  145. * This number is multiplied by the result of <code>czm_getLambertDiffuse</code> in GlobeFS.glsl.
  146. * This only takes effect when <code>enableLighting</code> is <code>true</code>.
  147. *
  148. * @type {number}
  149. * @default 0.9
  150. */
  151. this.lambertDiffuseMultiplier = 0.9;
  152. /**
  153. * Enable dynamic lighting effects on atmosphere and fog. This only takes effect
  154. * when <code>enableLighting</code> is <code>true</code>.
  155. *
  156. * @type {boolean}
  157. * @default true
  158. */
  159. this.dynamicAtmosphereLighting = true;
  160. /**
  161. * Whether dynamic atmosphere lighting uses the sun direction instead of the scene's
  162. * light direction. This only takes effect when <code>enableLighting</code> and
  163. * <code>dynamicAtmosphereLighting</code> are <code>true</code>.
  164. *
  165. * @type {boolean}
  166. * @default false
  167. */
  168. this.dynamicAtmosphereLightingFromSun = false;
  169. /**
  170. * Enable the ground atmosphere, which is drawn over the globe when viewed from a distance between <code>lightingFadeInDistance</code> and <code>lightingFadeOutDistance</code>.
  171. *
  172. * @type {boolean}
  173. * @default true when using the WGS84 ellipsoid, false otherwise
  174. */
  175. this.showGroundAtmosphere = Ellipsoid.WGS84.equals(ellipsoid);
  176. /**
  177. * The intensity of the light that is used for computing the ground atmosphere color.
  178. *
  179. * @type {number}
  180. * @default 10.0
  181. */
  182. this.atmosphereLightIntensity = 10.0;
  183. /**
  184. * The Rayleigh scattering coefficient used in the atmospheric scattering equations for the ground atmosphere.
  185. *
  186. * @type {Cartesian3}
  187. * @default Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
  188. */
  189. this.atmosphereRayleighCoefficient = new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6);
  190. /**
  191. * The Mie scattering coefficient used in the atmospheric scattering equations for the ground atmosphere.
  192. *
  193. * @type {Cartesian3}
  194. * @default Cartesian3(21e-6, 21e-6, 21e-6)
  195. */
  196. this.atmosphereMieCoefficient = new Cartesian3(21e-6, 21e-6, 21e-6);
  197. /**
  198. * The Rayleigh scale height used in the atmospheric scattering equations for the ground atmosphere, in meters.
  199. *
  200. * @type {number}
  201. * @default 10000.0
  202. */
  203. this.atmosphereRayleighScaleHeight = 10000.0;
  204. /**
  205. * The Mie scale height used in the atmospheric scattering equations for the ground atmosphere, in meters.
  206. *
  207. * @type {number}
  208. * @default 3200.0
  209. */
  210. this.atmosphereMieScaleHeight = 3200.0;
  211. /**
  212. * The anisotropy of the medium to consider for Mie scattering.
  213. * <p>
  214. * Valid values are between -1.0 and 1.0.
  215. * </p>
  216. * @type {number}
  217. * @default 0.9
  218. */
  219. this.atmosphereMieAnisotropy = 0.9;
  220. /**
  221. * The distance where everything becomes lit. This only takes effect
  222. * when <code>enableLighting</code> or <code>showGroundAtmosphere</code> is <code>true</code>.
  223. *
  224. * @type {number}
  225. * @default 1/2 * pi * ellipsoid.minimumRadius
  226. */
  227. this.lightingFadeOutDistance =
  228. CesiumMath.PI_OVER_TWO * ellipsoid.minimumRadius;
  229. /**
  230. * The distance where lighting resumes. This only takes effect
  231. * when <code>enableLighting</code> or <code>showGroundAtmosphere</code> is <code>true</code>.
  232. *
  233. * @type {number}
  234. * @default pi * ellipsoid.minimumRadius
  235. */
  236. this.lightingFadeInDistance = CesiumMath.PI * ellipsoid.minimumRadius;
  237. /**
  238. * The distance where the darkness of night from the ground atmosphere fades out to a lit ground atmosphere.
  239. * This only takes effect when <code>showGroundAtmosphere</code>, <code>enableLighting</code>, and
  240. * <code>dynamicAtmosphereLighting</code> are <code>true</code>.
  241. *
  242. * @type {number}
  243. * @default 1/2 * pi * ellipsoid.minimumRadius
  244. */
  245. this.nightFadeOutDistance = CesiumMath.PI_OVER_TWO * ellipsoid.minimumRadius;
  246. /**
  247. * The distance where the darkness of night from the ground atmosphere fades in to an unlit ground atmosphere.
  248. * This only takes effect when <code>showGroundAtmosphere</code>, <code>enableLighting</code>, and
  249. * <code>dynamicAtmosphereLighting</code> are <code>true</code>.
  250. *
  251. * @type {number}
  252. * @default 5/2 * pi * ellipsoid.minimumRadius
  253. */
  254. this.nightFadeInDistance =
  255. 5.0 * CesiumMath.PI_OVER_TWO * ellipsoid.minimumRadius;
  256. /**
  257. * True if an animated wave effect should be shown in areas of the globe
  258. * covered by water; otherwise, false. This property is ignored if the
  259. * <code>terrainProvider</code> does not provide a water mask.
  260. *
  261. * @type {boolean}
  262. * @default true
  263. */
  264. this.showWaterEffect = true;
  265. /**
  266. * True if primitives such as billboards, polylines, labels, etc. should be depth-tested
  267. * against the terrain surface, or false if such primitives should always be drawn on top
  268. * of terrain unless they're on the opposite side of the globe. The disadvantage of depth
  269. * testing primitives against terrain is that slight numerical noise or terrain level-of-detail
  270. * switched can sometimes make a primitive that should be on the surface disappear underneath it.
  271. *
  272. * @type {boolean}
  273. * @default false
  274. *
  275. */
  276. this.depthTestAgainstTerrain = false;
  277. /**
  278. * Determines whether the globe casts or receives shadows from light sources. Setting the globe
  279. * to cast shadows may impact performance since the terrain is rendered again from the light's perspective.
  280. * Currently only terrain that is in view casts shadows. By default the globe does not cast shadows.
  281. *
  282. * @type {ShadowMode}
  283. * @default ShadowMode.RECEIVE_ONLY
  284. */
  285. this.shadows = ShadowMode.RECEIVE_ONLY;
  286. /**
  287. * The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  288. * A hue shift of 1.0 indicates a complete rotation of the hues available.
  289. * @type {number}
  290. * @default 0.0
  291. */
  292. this.atmosphereHueShift = 0.0;
  293. /**
  294. * The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  295. * A saturation shift of -1.0 is monochrome.
  296. * @type {number}
  297. * @default 0.0
  298. */
  299. this.atmosphereSaturationShift = 0.0;
  300. /**
  301. * The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  302. * A brightness shift of -1.0 is complete darkness, which will let space show through.
  303. * @type {number}
  304. * @default 0.0
  305. */
  306. this.atmosphereBrightnessShift = 0.0;
  307. /**
  308. * Whether to show terrain skirts. Terrain skirts are geometry extending downwards from a tile's edges used to hide seams between neighboring tiles.
  309. * Skirts are always hidden when the camera is underground or translucency is enabled.
  310. *
  311. * @type {boolean}
  312. * @default true
  313. */
  314. this.showSkirts = true;
  315. /**
  316. * Whether to cull back-facing terrain. Back faces are not culled when the camera is underground or translucency is enabled.
  317. *
  318. * @type {boolean}
  319. * @default true
  320. */
  321. this.backFaceCulling = true;
  322. this._oceanNormalMap = undefined;
  323. this._zoomedOutOceanSpecularIntensity = undefined;
  324. /**
  325. * Determines the darkness of the vertex shadow.
  326. * This only takes effect when <code>enableLighting</code> is <code>true</code>.
  327. *
  328. * @type {number}
  329. * @default 0.3
  330. */
  331. this.vertexShadowDarkness = 0.3;
  332. }
  333. Object.defineProperties(Globe.prototype, {
  334. /**
  335. * Gets an ellipsoid describing the shape of this globe.
  336. * @memberof Globe.prototype
  337. * @type {Ellipsoid}
  338. */
  339. ellipsoid: {
  340. get: function () {
  341. return this._ellipsoid;
  342. },
  343. },
  344. /**
  345. * Gets the collection of image layers that will be rendered on this globe.
  346. * @memberof Globe.prototype
  347. * @type {ImageryLayerCollection}
  348. */
  349. imageryLayers: {
  350. get: function () {
  351. return this._imageryLayerCollection;
  352. },
  353. },
  354. /**
  355. * Gets an event that's raised when an imagery layer is added, shown, hidden, moved, or removed.
  356. *
  357. * @memberof Globe.prototype
  358. * @type {Event}
  359. * @readonly
  360. */
  361. imageryLayersUpdatedEvent: {
  362. get: function () {
  363. return this._surface.tileProvider.imageryLayersUpdatedEvent;
  364. },
  365. },
  366. /**
  367. * Returns <code>true</code> when the tile load queue is empty, <code>false</code> otherwise. When the load queue is empty,
  368. * all terrain and imagery for the current view have been loaded.
  369. * @memberof Globe.prototype
  370. * @type {boolean}
  371. * @readonly
  372. */
  373. tilesLoaded: {
  374. get: function () {
  375. if (!defined(this._surface)) {
  376. return true;
  377. }
  378. return (
  379. this._surface._tileLoadQueueHigh.length === 0 &&
  380. this._surface._tileLoadQueueMedium.length === 0 &&
  381. this._surface._tileLoadQueueLow.length === 0
  382. );
  383. },
  384. },
  385. /**
  386. * Gets or sets the color of the globe when no imagery is available.
  387. * @memberof Globe.prototype
  388. * @type {Color}
  389. */
  390. baseColor: {
  391. get: function () {
  392. return this._surface.tileProvider.baseColor;
  393. },
  394. set: function (value) {
  395. this._surface.tileProvider.baseColor = value;
  396. },
  397. },
  398. /**
  399. * A property specifying a {@link ClippingPlaneCollection} used to selectively disable rendering on the outside of each plane.
  400. *
  401. * @memberof Globe.prototype
  402. * @type {ClippingPlaneCollection}
  403. */
  404. clippingPlanes: {
  405. get: function () {
  406. return this._surface.tileProvider.clippingPlanes;
  407. },
  408. set: function (value) {
  409. this._surface.tileProvider.clippingPlanes = value;
  410. },
  411. },
  412. /**
  413. * A property specifying a {@link ClippingPolygonCollection} used to selectively disable rendering inside or outside a list of polygons.
  414. *
  415. * @memberof Globe.prototype
  416. * @type {ClippingPolygonCollection}
  417. */
  418. clippingPolygons: {
  419. get: function () {
  420. return this._surface.tileProvider.clippingPolygons;
  421. },
  422. set: function (value) {
  423. this._surface.tileProvider.clippingPolygons = value;
  424. },
  425. },
  426. /**
  427. * A property specifying a {@link Rectangle} used to limit globe rendering to a cartographic area.
  428. * Defaults to the maximum extent of cartographic coordinates.
  429. *
  430. * @memberof Globe.prototype
  431. * @type {Rectangle}
  432. * @default {@link Rectangle.MAX_VALUE}
  433. */
  434. cartographicLimitRectangle: {
  435. get: function () {
  436. return this._surface.tileProvider.cartographicLimitRectangle;
  437. },
  438. set: function (value) {
  439. if (!defined(value)) {
  440. value = Rectangle.clone(Rectangle.MAX_VALUE);
  441. }
  442. this._surface.tileProvider.cartographicLimitRectangle = value;
  443. },
  444. },
  445. /**
  446. * The normal map to use for rendering waves in the ocean. Setting this property will
  447. * only have an effect if the configured terrain provider includes a water mask.
  448. * @memberof Globe.prototype
  449. * @type {string}
  450. * @default buildModuleUrl('Assets/Textures/waterNormalsSmall.jpg')
  451. */
  452. oceanNormalMapUrl: {
  453. get: function () {
  454. return this._oceanNormalMapResource.url;
  455. },
  456. set: function (value) {
  457. this._oceanNormalMapResource.url = value;
  458. this._oceanNormalMapResourceDirty = true;
  459. },
  460. },
  461. /**
  462. * The terrain provider providing surface geometry for this globe.
  463. * @type {TerrainProvider}
  464. *
  465. * @memberof Globe.prototype
  466. * @type {TerrainProvider}
  467. *
  468. */
  469. terrainProvider: {
  470. get: function () {
  471. return this._terrainProvider;
  472. },
  473. set: function (value) {
  474. if (value !== this._terrainProvider) {
  475. this._terrainProvider = value;
  476. this._terrainProviderChanged.raiseEvent(value);
  477. if (defined(this._material)) {
  478. makeShadersDirty(this);
  479. }
  480. }
  481. },
  482. },
  483. /**
  484. * Gets an event that's raised when the terrain provider is changed
  485. *
  486. * @memberof Globe.prototype
  487. * @type {Event}
  488. * @readonly
  489. */
  490. terrainProviderChanged: {
  491. get: function () {
  492. return this._terrainProviderChanged;
  493. },
  494. },
  495. /**
  496. * Gets an event that's raised when the length of the tile load queue has changed since the last render frame. When the load queue is empty,
  497. * all terrain and imagery for the current view have been loaded. The event passes the new length of the tile load queue.
  498. *
  499. * @memberof Globe.prototype
  500. * @type {Event}
  501. */
  502. tileLoadProgressEvent: {
  503. get: function () {
  504. return this._surface.tileLoadProgressEvent;
  505. },
  506. },
  507. /**
  508. * Gets or sets the material appearance of the Globe. This can be one of several built-in {@link Material} objects or a custom material, scripted with
  509. * {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric}.
  510. * @memberof Globe.prototype
  511. * @type {Material | undefined}
  512. */
  513. material: {
  514. get: function () {
  515. return this._material;
  516. },
  517. set: function (material) {
  518. if (this._material !== material) {
  519. this._material = material;
  520. makeShadersDirty(this);
  521. }
  522. },
  523. },
  524. /**
  525. * The color to render the back side of the globe when the camera is underground or the globe is translucent,
  526. * blended with the globe color based on the camera's distance.
  527. * <br /><br />
  528. * To disable underground coloring, set <code>undergroundColor</code> to <code>undefined</code>.
  529. *
  530. * @memberof Globe.prototype
  531. * @type {Color}
  532. * @default {@link Color.BLACK}
  533. *
  534. * @see Globe#undergroundColorAlphaByDistance
  535. */
  536. undergroundColor: {
  537. get: function () {
  538. return this._undergroundColor;
  539. },
  540. set: function (value) {
  541. this._undergroundColor = Color.clone(value, this._undergroundColor);
  542. },
  543. },
  544. /**
  545. * Gets or sets the near and far distance for blending {@link Globe#undergroundColor} with the globe color.
  546. * The alpha will interpolate between the {@link NearFarScalar#nearValue} and
  547. * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds
  548. * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}.
  549. * Outside of these ranges the alpha remains clamped to the nearest bound. If undefined,
  550. * the underground color will not be blended with the globe color.
  551. * <br /> <br />
  552. * When the camera is above the ellipsoid the distance is computed from the nearest
  553. * point on the ellipsoid instead of the camera's position.
  554. *
  555. * @memberof Globe.prototype
  556. * @type {NearFarScalar}
  557. *
  558. * @see Globe#undergroundColor
  559. *
  560. */
  561. undergroundColorAlphaByDistance: {
  562. get: function () {
  563. return this._undergroundColorAlphaByDistance;
  564. },
  565. set: function (value) {
  566. //>>includeStart('debug', pragmas.debug);
  567. if (defined(value) && value.far < value.near) {
  568. throw new DeveloperError(
  569. "far distance must be greater than near distance.",
  570. );
  571. }
  572. //>>includeEnd('debug');
  573. this._undergroundColorAlphaByDistance = NearFarScalar.clone(
  574. value,
  575. this._undergroundColorAlphaByDistance,
  576. );
  577. },
  578. },
  579. /**
  580. * Properties for controlling globe translucency.
  581. *
  582. * @memberof Globe.prototype
  583. * @type {GlobeTranslucency}
  584. */
  585. translucency: {
  586. get: function () {
  587. return this._translucency;
  588. },
  589. },
  590. });
  591. function makeShadersDirty(globe) {
  592. const defines = [];
  593. const requireNormals =
  594. defined(globe._material) &&
  595. (defined(globe._material.shaderSource.match(/slope/)) ||
  596. defined(globe._material.shaderSource.match("normalEC")));
  597. const fragmentSources = [AtmosphereCommon, GroundAtmosphere];
  598. if (
  599. defined(globe._material) &&
  600. (!requireNormals || globe._terrainProvider.hasVertexNormals)
  601. ) {
  602. fragmentSources.push(globe._material.shaderSource);
  603. defines.push("APPLY_MATERIAL");
  604. globe._surface._tileProvider.materialUniformMap = globe._material._uniforms;
  605. } else {
  606. globe._surface._tileProvider.materialUniformMap = undefined;
  607. }
  608. fragmentSources.push(GlobeFS);
  609. globe._surfaceShaderSet.baseVertexShaderSource = new ShaderSource({
  610. sources: [AtmosphereCommon, GroundAtmosphere, GlobeVS],
  611. defines: defines,
  612. });
  613. globe._surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
  614. sources: fragmentSources,
  615. defines: defines,
  616. });
  617. globe._surfaceShaderSet.material = globe._material;
  618. }
  619. function createComparePickTileFunction(rayOrigin) {
  620. return function (a, b) {
  621. const aDist = BoundingSphere.distanceSquaredTo(
  622. a.pickBoundingSphere,
  623. rayOrigin,
  624. );
  625. const bDist = BoundingSphere.distanceSquaredTo(
  626. b.pickBoundingSphere,
  627. rayOrigin,
  628. );
  629. return aDist - bDist;
  630. };
  631. }
  632. const scratchArray = [];
  633. const scratchSphereIntersectionResult = {
  634. start: 0.0,
  635. stop: 0.0,
  636. };
  637. /**
  638. * Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates.
  639. *
  640. * @param {Ray} ray The ray to test for intersection.
  641. * @param {Scene} scene The scene.
  642. * @param {boolean} [cullBackFaces=true] Set to true to not pick back faces.
  643. * @param {Cartesian3} [result] The object onto which to store the result.
  644. * @returns {Cartesian3|undefined} The intersection or <code>undefined</code> if none was found. The returned position is in projected coordinates for 2D and Columbus View.
  645. *
  646. * @private
  647. */
  648. Globe.prototype.pickWorldCoordinates = function (
  649. ray,
  650. scene,
  651. cullBackFaces,
  652. result,
  653. ) {
  654. //>>includeStart('debug', pragmas.debug);
  655. if (!defined(ray)) {
  656. throw new DeveloperError("ray is required");
  657. }
  658. if (!defined(scene)) {
  659. throw new DeveloperError("scene is required");
  660. }
  661. //>>includeEnd('debug');
  662. cullBackFaces = cullBackFaces ?? true;
  663. const mode = scene.mode;
  664. const projection = scene.mapProjection;
  665. const sphereIntersections = scratchArray;
  666. sphereIntersections.length = 0;
  667. for (const tile of this._surface._tilesRenderedThisFrame) {
  668. const surfaceTile = tile.data;
  669. if (!defined(surfaceTile)) {
  670. continue;
  671. }
  672. let boundingVolume = surfaceTile.pickBoundingSphere;
  673. if (mode !== SceneMode.SCENE3D) {
  674. surfaceTile.pickBoundingSphere = boundingVolume =
  675. BoundingSphere.fromRectangleWithHeights2D(
  676. tile.rectangle,
  677. projection,
  678. surfaceTile.tileBoundingRegion.minimumHeight,
  679. surfaceTile.tileBoundingRegion.maximumHeight,
  680. boundingVolume,
  681. );
  682. Cartesian3.fromElements(
  683. boundingVolume.center.z,
  684. boundingVolume.center.x,
  685. boundingVolume.center.y,
  686. boundingVolume.center,
  687. );
  688. } else if (defined(surfaceTile.renderedMesh)) {
  689. BoundingSphere.clone(
  690. surfaceTile.tileBoundingRegion.boundingSphere,
  691. boundingVolume,
  692. );
  693. } else {
  694. // So wait how did we render this thing then? It shouldn't be possible to get here.
  695. continue;
  696. }
  697. const boundingSphereIntersection = IntersectionTests.raySphere(
  698. ray,
  699. boundingVolume,
  700. scratchSphereIntersectionResult,
  701. );
  702. if (defined(boundingSphereIntersection)) {
  703. sphereIntersections.push(surfaceTile);
  704. }
  705. }
  706. sphereIntersections.sort(createComparePickTileFunction(ray.origin));
  707. let intersection;
  708. const length = sphereIntersections.length;
  709. for (let i = 0; i < length; ++i) {
  710. intersection = sphereIntersections[i].pick(
  711. ray,
  712. scene.mode,
  713. scene.mapProjection,
  714. cullBackFaces,
  715. result,
  716. );
  717. if (defined(intersection)) {
  718. break;
  719. }
  720. }
  721. return intersection;
  722. };
  723. const cartoScratch = new Cartographic();
  724. /**
  725. * Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates.
  726. *
  727. * @param {Ray} ray The ray to test for intersection.
  728. * @param {Scene} scene The scene.
  729. * @param {Cartesian3} [result] The object onto which to store the result.
  730. * @returns {Cartesian3|undefined} The intersection or <code>undefined</code> if none was found.
  731. *
  732. * @example
  733. * // find intersection of ray through a pixel and the globe
  734. * const ray = viewer.camera.getPickRay(windowCoordinates);
  735. * const intersection = globe.pick(ray, scene);
  736. */
  737. Globe.prototype.pick = function (ray, scene, result) {
  738. result = this.pickWorldCoordinates(ray, scene, true, result);
  739. if (defined(result) && scene.mode !== SceneMode.SCENE3D) {
  740. result = Cartesian3.fromElements(result.y, result.z, result.x, result);
  741. const carto = scene.mapProjection.unproject(result, cartoScratch);
  742. result = this._ellipsoid.cartographicToCartesian(carto, result);
  743. }
  744. return result;
  745. };
  746. const scratchGetHeightCartesian = new Cartesian3();
  747. const scratchGetHeightIntersection = new Cartesian3();
  748. const scratchGetHeightCartographic = new Cartographic();
  749. const scratchGetHeightRay = new Ray();
  750. function tileIfContainsCartographic(tile, cartographic) {
  751. return defined(tile) && Rectangle.contains(tile.rectangle, cartographic)
  752. ? tile
  753. : undefined;
  754. }
  755. /**
  756. * Get the height of the surface at a given cartographic.
  757. *
  758. * @param {Cartographic} cartographic The cartographic for which to find the height.
  759. * @returns {number|undefined} The height of the cartographic or undefined if it could not be found.
  760. */
  761. Globe.prototype.getHeight = function (cartographic) {
  762. //>>includeStart('debug', pragmas.debug);
  763. if (!defined(cartographic)) {
  764. throw new DeveloperError("cartographic is required");
  765. }
  766. //>>includeEnd('debug');
  767. const levelZeroTiles = this._surface._levelZeroTiles;
  768. if (!defined(levelZeroTiles)) {
  769. return;
  770. }
  771. let tile;
  772. let i;
  773. const length = levelZeroTiles.length;
  774. for (i = 0; i < length; ++i) {
  775. tile = levelZeroTiles[i];
  776. if (Rectangle.contains(tile.rectangle, cartographic)) {
  777. break;
  778. }
  779. }
  780. if (i >= length) {
  781. return undefined;
  782. }
  783. let tileWithMesh = tile;
  784. while (defined(tile)) {
  785. tile =
  786. tileIfContainsCartographic(tile._southwestChild, cartographic) ||
  787. tileIfContainsCartographic(tile._southeastChild, cartographic) ||
  788. tileIfContainsCartographic(tile._northwestChild, cartographic) ||
  789. tile._northeastChild;
  790. if (
  791. defined(tile) &&
  792. defined(tile.data) &&
  793. defined(tile.data.renderedMesh)
  794. ) {
  795. tileWithMesh = tile;
  796. }
  797. }
  798. tile = tileWithMesh;
  799. // This tile was either rendered or culled.
  800. // It is sometimes useful to get a height from a culled tile,
  801. // e.g. when we're getting a height in order to place a billboard
  802. // on terrain, and the camera is looking at that same billboard.
  803. // The culled tile must have a valid mesh, though.
  804. if (
  805. !defined(tile) ||
  806. !defined(tile.data) ||
  807. !defined(tile.data.renderedMesh)
  808. ) {
  809. // Tile was not rendered (culled).
  810. return undefined;
  811. }
  812. const projection = this._surface._tileProvider.tilingScheme.projection;
  813. const ellipsoid = this._surface._tileProvider.tilingScheme.ellipsoid;
  814. //cartesian has to be on the ellipsoid surface for `ellipsoid.geodeticSurfaceNormal`
  815. const cartesian = Cartesian3.fromRadians(
  816. cartographic.longitude,
  817. cartographic.latitude,
  818. 0.0,
  819. ellipsoid,
  820. scratchGetHeightCartesian,
  821. );
  822. const ray = scratchGetHeightRay;
  823. const surfaceNormal = ellipsoid.geodeticSurfaceNormal(
  824. cartesian,
  825. ray.direction,
  826. );
  827. // Try to find the intersection point between the surface normal and z-axis.
  828. // minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
  829. const rayOrigin = ellipsoid.getSurfaceNormalIntersectionWithZAxis(
  830. cartesian,
  831. 11500.0,
  832. ray.origin,
  833. );
  834. // Theoretically, not with Earth datums, the intersection point can be outside the ellipsoid
  835. if (!defined(rayOrigin)) {
  836. // intersection point is outside the ellipsoid, try other value
  837. // minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
  838. let minimumHeight;
  839. if (defined(tile.data.tileBoundingRegion)) {
  840. minimumHeight = tile.data.tileBoundingRegion.minimumHeight;
  841. }
  842. const magnitude = Math.min(minimumHeight ?? 0.0, -11500.0);
  843. // multiply by the *positive* value of the magnitude
  844. const vectorToMinimumPoint = Cartesian3.multiplyByScalar(
  845. surfaceNormal,
  846. Math.abs(magnitude) + 1,
  847. scratchGetHeightIntersection,
  848. );
  849. Cartesian3.subtract(cartesian, vectorToMinimumPoint, ray.origin);
  850. }
  851. const intersection = tile.data.pick(
  852. ray,
  853. // Globe height is the same at a given cartographic regardless of the scene mode,
  854. // but the ray is constructed via a surface normal (which assumes 3D), so pick in 3D mode.
  855. SceneMode.SCENE3D,
  856. projection,
  857. false,
  858. scratchGetHeightIntersection,
  859. );
  860. if (!defined(intersection)) {
  861. return undefined;
  862. }
  863. return ellipsoid.cartesianToCartographic(
  864. intersection,
  865. scratchGetHeightCartographic,
  866. ).height;
  867. };
  868. /**
  869. * @private
  870. */
  871. Globe.prototype.update = function (frameState) {
  872. if (!this.show) {
  873. return;
  874. }
  875. if (frameState.passes.render) {
  876. this._surface.update(frameState);
  877. }
  878. };
  879. /**
  880. * @private
  881. */
  882. Globe.prototype.beginFrame = function (frameState) {
  883. const surface = this._surface;
  884. const tileProvider = surface.tileProvider;
  885. const terrainProvider = this.terrainProvider;
  886. const hasWaterMask =
  887. defined(terrainProvider) &&
  888. terrainProvider.hasWaterMask &&
  889. terrainProvider.hasWaterMask;
  890. if (hasWaterMask && this._oceanNormalMapResourceDirty) {
  891. // url changed, load new normal map asynchronously
  892. this._oceanNormalMapResourceDirty = false;
  893. const oceanNormalMapResource = this._oceanNormalMapResource;
  894. const oceanNormalMapUrl = oceanNormalMapResource.url;
  895. if (defined(oceanNormalMapUrl)) {
  896. const that = this;
  897. oceanNormalMapResource.fetchImage().then(function (image) {
  898. if (oceanNormalMapUrl !== that._oceanNormalMapResource.url) {
  899. // url changed while we were loading
  900. return;
  901. }
  902. that._oceanNormalMap =
  903. that._oceanNormalMap && that._oceanNormalMap.destroy();
  904. that._oceanNormalMap = new Texture({
  905. context: frameState.context,
  906. source: image,
  907. });
  908. });
  909. } else {
  910. this._oceanNormalMap =
  911. this._oceanNormalMap && this._oceanNormalMap.destroy();
  912. }
  913. }
  914. const pass = frameState.passes;
  915. const mode = frameState.mode;
  916. if (pass.render) {
  917. if (this.showGroundAtmosphere) {
  918. this._zoomedOutOceanSpecularIntensity = 0.4;
  919. } else {
  920. this._zoomedOutOceanSpecularIntensity = 0.5;
  921. }
  922. surface.maximumScreenSpaceError = this.maximumScreenSpaceError;
  923. surface.tileCacheSize = this.tileCacheSize;
  924. surface.loadingDescendantLimit = this.loadingDescendantLimit;
  925. surface.preloadAncestors = this.preloadAncestors;
  926. surface.preloadSiblings = this.preloadSiblings;
  927. tileProvider.terrainProvider = this.terrainProvider;
  928. tileProvider.lightingFadeOutDistance = this.lightingFadeOutDistance;
  929. tileProvider.lightingFadeInDistance = this.lightingFadeInDistance;
  930. tileProvider.nightFadeOutDistance = this.nightFadeOutDistance;
  931. tileProvider.nightFadeInDistance = this.nightFadeInDistance;
  932. tileProvider.zoomedOutOceanSpecularIntensity =
  933. mode === SceneMode.SCENE3D ? this._zoomedOutOceanSpecularIntensity : 0.0;
  934. tileProvider.hasWaterMask = hasWaterMask;
  935. tileProvider.showWaterEffect = this.showWaterEffect;
  936. tileProvider.oceanNormalMap = this._oceanNormalMap;
  937. tileProvider.enableLighting = this.enableLighting;
  938. tileProvider.dynamicAtmosphereLighting = this.dynamicAtmosphereLighting;
  939. tileProvider.dynamicAtmosphereLightingFromSun =
  940. this.dynamicAtmosphereLightingFromSun;
  941. tileProvider.showGroundAtmosphere = this.showGroundAtmosphere;
  942. tileProvider.atmosphereLightIntensity = this.atmosphereLightIntensity;
  943. tileProvider.atmosphereRayleighCoefficient =
  944. this.atmosphereRayleighCoefficient;
  945. tileProvider.atmosphereMieCoefficient = this.atmosphereMieCoefficient;
  946. tileProvider.atmosphereRayleighScaleHeight =
  947. this.atmosphereRayleighScaleHeight;
  948. tileProvider.atmosphereMieScaleHeight = this.atmosphereMieScaleHeight;
  949. tileProvider.atmosphereMieAnisotropy = this.atmosphereMieAnisotropy;
  950. tileProvider.shadows = this.shadows;
  951. tileProvider.hueShift = this.atmosphereHueShift;
  952. tileProvider.saturationShift = this.atmosphereSaturationShift;
  953. tileProvider.brightnessShift = this.atmosphereBrightnessShift;
  954. tileProvider.fillHighlightColor = this.fillHighlightColor;
  955. tileProvider.showSkirts = this.showSkirts;
  956. tileProvider.backFaceCulling = this.backFaceCulling;
  957. tileProvider.vertexShadowDarkness = this.vertexShadowDarkness;
  958. tileProvider.undergroundColor = this._undergroundColor;
  959. tileProvider.undergroundColorAlphaByDistance =
  960. this._undergroundColorAlphaByDistance;
  961. tileProvider.lambertDiffuseMultiplier = this.lambertDiffuseMultiplier;
  962. surface.beginFrame(frameState);
  963. }
  964. };
  965. /**
  966. * @private
  967. */
  968. Globe.prototype.render = function (frameState) {
  969. if (!this.show) {
  970. return;
  971. }
  972. if (defined(this._material)) {
  973. this._material.update(frameState.context);
  974. }
  975. this._surface.render(frameState);
  976. };
  977. /**
  978. * @private
  979. */
  980. Globe.prototype.endFrame = function (frameState) {
  981. if (!this.show) {
  982. return;
  983. }
  984. if (frameState.passes.render) {
  985. this._surface.endFrame(frameState);
  986. }
  987. };
  988. /**
  989. * Returns true if this object was destroyed; otherwise, false.
  990. * <br /><br />
  991. * If this object was destroyed, it should not be used; calling any function other than
  992. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  993. *
  994. * @returns {boolean} True if this object was destroyed; otherwise, false.
  995. *
  996. * @see Globe#destroy
  997. */
  998. Globe.prototype.isDestroyed = function () {
  999. return false;
  1000. };
  1001. /**
  1002. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1003. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1004. * <br /><br />
  1005. * Once an object is destroyed, it should not be used; calling any function other than
  1006. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1007. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1008. *
  1009. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1010. *
  1011. *
  1012. * @example
  1013. * globe = globe && globe.destroy();
  1014. *
  1015. * @see Globe#isDestroyed
  1016. */
  1017. Globe.prototype.destroy = function () {
  1018. this._surfaceShaderSet =
  1019. this._surfaceShaderSet && this._surfaceShaderSet.destroy();
  1020. this._surface = this._surface && this._surface.destroy();
  1021. this._oceanNormalMap = this._oceanNormalMap && this._oceanNormalMap.destroy();
  1022. return destroyObject(this);
  1023. };
  1024. export default Globe;