GlobeSurfaceTileProvider.js 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939
  1. import BoundingSphere from '../Core/BoundingSphere.js';
  2. import BoxOutlineGeometry from '../Core/BoxOutlineGeometry.js';
  3. import Cartesian2 from '../Core/Cartesian2.js';
  4. import Cartesian3 from '../Core/Cartesian3.js';
  5. import Cartesian4 from '../Core/Cartesian4.js';
  6. import Cartographic from '../Core/Cartographic.js';
  7. import Color from '../Core/Color.js';
  8. import ColorGeometryInstanceAttribute from '../Core/ColorGeometryInstanceAttribute.js';
  9. import combine from '../Core/combine.js';
  10. import defaultValue from '../Core/defaultValue.js';
  11. import defined from '../Core/defined.js';
  12. import defineProperties from '../Core/defineProperties.js';
  13. import destroyObject from '../Core/destroyObject.js';
  14. import DeveloperError from '../Core/DeveloperError.js';
  15. import Event from '../Core/Event.js';
  16. import GeometryInstance from '../Core/GeometryInstance.js';
  17. import GeometryPipeline from '../Core/GeometryPipeline.js';
  18. import IndexDatatype from '../Core/IndexDatatype.js';
  19. import Intersect from '../Core/Intersect.js';
  20. import CesiumMath from '../Core/Math.js';
  21. import Matrix4 from '../Core/Matrix4.js';
  22. import OrientedBoundingBox from '../Core/OrientedBoundingBox.js';
  23. import OrthographicFrustum from '../Core/OrthographicFrustum.js';
  24. import PrimitiveType from '../Core/PrimitiveType.js';
  25. import Rectangle from '../Core/Rectangle.js';
  26. import SphereOutlineGeometry from '../Core/SphereOutlineGeometry.js';
  27. import TerrainQuantization from '../Core/TerrainQuantization.js';
  28. import Visibility from '../Core/Visibility.js';
  29. import WebMercatorProjection from '../Core/WebMercatorProjection.js';
  30. import Buffer from '../Renderer/Buffer.js';
  31. import BufferUsage from '../Renderer/BufferUsage.js';
  32. import ContextLimits from '../Renderer/ContextLimits.js';
  33. import DrawCommand from '../Renderer/DrawCommand.js';
  34. import Pass from '../Renderer/Pass.js';
  35. import RenderState from '../Renderer/RenderState.js';
  36. import VertexArray from '../Renderer/VertexArray.js';
  37. import BlendingState from './BlendingState.js';
  38. import ClippingPlaneCollection from './ClippingPlaneCollection.js';
  39. import DepthFunction from './DepthFunction.js';
  40. import GlobeSurfaceTile from './GlobeSurfaceTile.js';
  41. import ImageryLayer from './ImageryLayer.js';
  42. import ImageryState from './ImageryState.js';
  43. import PerInstanceColorAppearance from './PerInstanceColorAppearance.js';
  44. import Primitive from './Primitive.js';
  45. import QuadtreeTileLoadState from './QuadtreeTileLoadState.js';
  46. import SceneMode from './SceneMode.js';
  47. import ShadowMode from './ShadowMode.js';
  48. import TerrainFillMesh from './TerrainFillMesh.js';
  49. import TerrainState from './TerrainState.js';
  50. import TileBoundingRegion from './TileBoundingRegion.js';
  51. import TileSelectionResult from './TileSelectionResult.js';
  52. /**
  53. * Provides quadtree tiles representing the surface of the globe. This type is intended to be used
  54. * with {@link QuadtreePrimitive}.
  55. *
  56. * @alias GlobeSurfaceTileProvider
  57. * @constructor
  58. *
  59. * @param {TerrainProvider} options.terrainProvider The terrain provider that describes the surface geometry.
  60. * @param {ImageryLayerCollection} option.imageryLayers The collection of imagery layers describing the shading of the surface.
  61. * @param {GlobeSurfaceShaderSet} options.surfaceShaderSet The set of shaders used to render the surface.
  62. *
  63. * @private
  64. */
  65. function GlobeSurfaceTileProvider(options) {
  66. //>>includeStart('debug', pragmas.debug);
  67. if (!defined(options)) {
  68. throw new DeveloperError('options is required.');
  69. }
  70. if (!defined(options.terrainProvider)) {
  71. throw new DeveloperError('options.terrainProvider is required.');
  72. } else if (!defined(options.imageryLayers)) {
  73. throw new DeveloperError('options.imageryLayers is required.');
  74. } else if (!defined(options.surfaceShaderSet)) {
  75. throw new DeveloperError('options.surfaceShaderSet is required.');
  76. }
  77. //>>includeEnd('debug');
  78. this.lightingFadeOutDistance = 6500000.0;
  79. this.lightingFadeInDistance = 9000000.0;
  80. this.hasWaterMask = false;
  81. this.oceanNormalMap = undefined;
  82. this.zoomedOutOceanSpecularIntensity = 0.5;
  83. this.enableLighting = false;
  84. this.showGroundAtmosphere = false;
  85. this.shadows = ShadowMode.RECEIVE_ONLY;
  86. /**
  87. * The color to use to highlight terrain fill tiles. If undefined, fill tiles are not
  88. * highlighted at all. The alpha value is used to alpha blend with the tile's
  89. * actual color. Because terrain fill tiles do not represent the actual terrain surface,
  90. * it may be useful in some applications to indicate visually that they are not to be trusted.
  91. * @type {Color}
  92. * @default undefined
  93. */
  94. this.fillHighlightColor = undefined;
  95. this.hueShift = 0.0;
  96. this.saturationShift = 0.0;
  97. this.brightnessShift = 0.0;
  98. this._quadtree = undefined;
  99. this._terrainProvider = options.terrainProvider;
  100. this._imageryLayers = options.imageryLayers;
  101. this._surfaceShaderSet = options.surfaceShaderSet;
  102. this._renderState = undefined;
  103. this._blendRenderState = undefined;
  104. this._errorEvent = new Event();
  105. this._imageryLayers.layerAdded.addEventListener(GlobeSurfaceTileProvider.prototype._onLayerAdded, this);
  106. this._imageryLayers.layerRemoved.addEventListener(GlobeSurfaceTileProvider.prototype._onLayerRemoved, this);
  107. this._imageryLayers.layerMoved.addEventListener(GlobeSurfaceTileProvider.prototype._onLayerMoved, this);
  108. this._imageryLayers.layerShownOrHidden.addEventListener(GlobeSurfaceTileProvider.prototype._onLayerShownOrHidden, this);
  109. this._imageryLayersUpdatedEvent = new Event();
  110. this._layerOrderChanged = false;
  111. this._tilesToRenderByTextureCount = [];
  112. this._drawCommands = [];
  113. this._uniformMaps = [];
  114. this._usedDrawCommands = 0;
  115. this._vertexArraysToDestroy = [];
  116. this._debug = {
  117. wireframe : false,
  118. boundingSphereTile : undefined
  119. };
  120. this._baseColor = undefined;
  121. this._firstPassInitialColor = undefined;
  122. this.baseColor = new Color(0.0, 0.0, 0.5, 1.0);
  123. /**
  124. * A property specifying a {@link ClippingPlaneCollection} used to selectively disable rendering on the outside of each plane.
  125. * @type {ClippingPlaneCollection}
  126. * @private
  127. */
  128. this._clippingPlanes = undefined;
  129. /**
  130. * A property specifying a {@link Rectangle} used to selectively limit terrain and imagery rendering.
  131. * @type {Rectangle}
  132. */
  133. this.cartographicLimitRectangle = Rectangle.clone(Rectangle.MAX_VALUE);
  134. this._hasLoadedTilesThisFrame = false;
  135. this._hasFillTilesThisFrame = false;
  136. }
  137. defineProperties(GlobeSurfaceTileProvider.prototype, {
  138. /**
  139. * Gets or sets the color of the globe when no imagery is available.
  140. * @memberof GlobeSurfaceTileProvider.prototype
  141. * @type {Color}
  142. */
  143. baseColor : {
  144. get : function() {
  145. return this._baseColor;
  146. },
  147. set : function(value) {
  148. //>>includeStart('debug', pragmas.debug);
  149. if (!defined(value)) {
  150. throw new DeveloperError('value is required.');
  151. }
  152. //>>includeEnd('debug');
  153. this._baseColor = value;
  154. this._firstPassInitialColor = Cartesian4.fromColor(value, this._firstPassInitialColor);
  155. }
  156. },
  157. /**
  158. * Gets or sets the {@link QuadtreePrimitive} for which this provider is
  159. * providing tiles. This property may be undefined if the provider is not yet associated
  160. * with a {@link QuadtreePrimitive}.
  161. * @memberof GlobeSurfaceTileProvider.prototype
  162. * @type {QuadtreePrimitive}
  163. */
  164. quadtree : {
  165. get : function() {
  166. return this._quadtree;
  167. },
  168. set : function(value) {
  169. //>>includeStart('debug', pragmas.debug);
  170. if (!defined(value)) {
  171. throw new DeveloperError('value is required.');
  172. }
  173. //>>includeEnd('debug');
  174. this._quadtree = value;
  175. }
  176. },
  177. /**
  178. * Gets a value indicating whether or not the provider is ready for use.
  179. * @memberof GlobeSurfaceTileProvider.prototype
  180. * @type {Boolean}
  181. */
  182. ready : {
  183. get : function() {
  184. return this._terrainProvider.ready && (this._imageryLayers.length === 0 || this._imageryLayers.get(0).imageryProvider.ready);
  185. }
  186. },
  187. /**
  188. * Gets the tiling scheme used by the provider. This property should
  189. * not be accessed before {@link GlobeSurfaceTileProvider#ready} returns true.
  190. * @memberof GlobeSurfaceTileProvider.prototype
  191. * @type {TilingScheme}
  192. */
  193. tilingScheme : {
  194. get : function() {
  195. return this._terrainProvider.tilingScheme;
  196. }
  197. },
  198. /**
  199. * Gets an event that is raised when the geometry provider encounters an asynchronous error. By subscribing
  200. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  201. * are passed an instance of {@link TileProviderError}.
  202. * @memberof GlobeSurfaceTileProvider.prototype
  203. * @type {Event}
  204. */
  205. errorEvent : {
  206. get : function() {
  207. return this._errorEvent;
  208. }
  209. },
  210. /**
  211. * Gets an event that is raised when an imagery layer is added, shown, hidden, moved, or removed.
  212. * @memberof GlobeSurfaceTileProvider.prototype
  213. * @type {Event}
  214. */
  215. imageryLayersUpdatedEvent : {
  216. get : function() {
  217. return this._imageryLayersUpdatedEvent;
  218. }
  219. },
  220. /**
  221. * Gets or sets the terrain provider that describes the surface geometry.
  222. * @memberof GlobeSurfaceTileProvider.prototype
  223. * @type {TerrainProvider}
  224. */
  225. terrainProvider : {
  226. get : function() {
  227. return this._terrainProvider;
  228. },
  229. set : function(terrainProvider) {
  230. if (this._terrainProvider === terrainProvider) {
  231. return;
  232. }
  233. //>>includeStart('debug', pragmas.debug);
  234. if (!defined(terrainProvider)) {
  235. throw new DeveloperError('terrainProvider is required.');
  236. }
  237. //>>includeEnd('debug');
  238. this._terrainProvider = terrainProvider;
  239. if (defined(this._quadtree)) {
  240. this._quadtree.invalidateAllTiles();
  241. }
  242. }
  243. },
  244. /**
  245. * The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset.
  246. *
  247. * @type {ClippingPlaneCollection}
  248. *
  249. * @private
  250. */
  251. clippingPlanes : {
  252. get : function() {
  253. return this._clippingPlanes;
  254. },
  255. set : function(value) {
  256. ClippingPlaneCollection.setOwner(value, this, '_clippingPlanes');
  257. }
  258. }
  259. });
  260. function sortTileImageryByLayerIndex(a, b) {
  261. var aImagery = a.loadingImagery;
  262. if (!defined(aImagery)) {
  263. aImagery = a.readyImagery;
  264. }
  265. var bImagery = b.loadingImagery;
  266. if (!defined(bImagery)) {
  267. bImagery = b.readyImagery;
  268. }
  269. return aImagery.imageryLayer._layerIndex - bImagery.imageryLayer._layerIndex;
  270. }
  271. /**
  272. * Make updates to the tile provider that are not involved in rendering. Called before the render update cycle.
  273. */
  274. GlobeSurfaceTileProvider.prototype.update = function(frameState) {
  275. // update collection: imagery indices, base layers, raise layer show/hide event
  276. this._imageryLayers._update();
  277. };
  278. function updateCredits(surface, frameState) {
  279. var creditDisplay = frameState.creditDisplay;
  280. if (surface._terrainProvider.ready && defined(surface._terrainProvider.credit)) {
  281. creditDisplay.addCredit(surface._terrainProvider.credit);
  282. }
  283. var imageryLayers = surface._imageryLayers;
  284. for (var i = 0, len = imageryLayers.length; i < len; ++i) {
  285. var imageryProvider = imageryLayers.get(i).imageryProvider;
  286. if (imageryProvider.ready && defined(imageryProvider.credit)) {
  287. creditDisplay.addCredit(imageryProvider.credit);
  288. }
  289. }
  290. }
  291. /**
  292. * Called at the beginning of each render frame, before {@link QuadtreeTileProvider#showTileThisFrame}
  293. * @param {FrameState} frameState The frame state.
  294. */
  295. GlobeSurfaceTileProvider.prototype.initialize = function(frameState) {
  296. // update each layer for texture reprojection.
  297. this._imageryLayers.queueReprojectionCommands(frameState);
  298. if (this._layerOrderChanged) {
  299. this._layerOrderChanged = false;
  300. // Sort the TileImagery instances in each tile by the layer index.
  301. this._quadtree.forEachLoadedTile(function(tile) {
  302. tile.data.imagery.sort(sortTileImageryByLayerIndex);
  303. });
  304. }
  305. // Add credits for terrain and imagery providers.
  306. updateCredits(this, frameState);
  307. var vertexArraysToDestroy = this._vertexArraysToDestroy;
  308. var length = vertexArraysToDestroy.length;
  309. for (var j = 0; j < length; ++j) {
  310. GlobeSurfaceTile._freeVertexArray(vertexArraysToDestroy[j]);
  311. }
  312. vertexArraysToDestroy.length = 0;
  313. };
  314. /**
  315. * Called at the beginning of the update cycle for each render frame, before {@link QuadtreeTileProvider#showTileThisFrame}
  316. * or any other functions.
  317. *
  318. * @param {FrameState} frameState The frame state.
  319. */
  320. GlobeSurfaceTileProvider.prototype.beginUpdate = function(frameState) {
  321. var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount;
  322. for (var i = 0, len = tilesToRenderByTextureCount.length; i < len; ++i) {
  323. var tiles = tilesToRenderByTextureCount[i];
  324. if (defined(tiles)) {
  325. tiles.length = 0;
  326. }
  327. }
  328. // update clipping planes
  329. var clippingPlanes = this._clippingPlanes;
  330. if (defined(clippingPlanes) && clippingPlanes.enabled) {
  331. clippingPlanes.update(frameState);
  332. }
  333. this._usedDrawCommands = 0;
  334. this._hasLoadedTilesThisFrame = false;
  335. this._hasFillTilesThisFrame = false;
  336. };
  337. /**
  338. * Called at the end of the update cycle for each render frame, after {@link QuadtreeTileProvider#showTileThisFrame}
  339. * and any other functions.
  340. *
  341. * @param {FrameState} frameState The frame state.
  342. */
  343. GlobeSurfaceTileProvider.prototype.endUpdate = function(frameState) {
  344. if (!defined(this._renderState)) {
  345. this._renderState = RenderState.fromCache({ // Write color and depth
  346. cull : {
  347. enabled : true
  348. },
  349. depthTest : {
  350. enabled : true,
  351. func : DepthFunction.LESS
  352. }
  353. });
  354. this._blendRenderState = RenderState.fromCache({ // Write color and depth
  355. cull : {
  356. enabled : true
  357. },
  358. depthTest : {
  359. enabled : true,
  360. func : DepthFunction.LESS_OR_EQUAL
  361. },
  362. blending : BlendingState.ALPHA_BLEND
  363. });
  364. }
  365. // If this frame has a mix of loaded and fill tiles, we need to propagate
  366. // loaded heights to the fill tiles.
  367. if (this._hasFillTilesThisFrame && this._hasLoadedTilesThisFrame) {
  368. TerrainFillMesh.updateFillTiles(this, this._quadtree._tilesToRender, frameState, this._vertexArraysToDestroy);
  369. }
  370. // Add the tile render commands to the command list, sorted by texture count.
  371. var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount;
  372. for (var textureCountIndex = 0, textureCountLength = tilesToRenderByTextureCount.length; textureCountIndex < textureCountLength; ++textureCountIndex) {
  373. var tilesToRender = tilesToRenderByTextureCount[textureCountIndex];
  374. if (!defined(tilesToRender)) {
  375. continue;
  376. }
  377. for (var tileIndex = 0, tileLength = tilesToRender.length; tileIndex < tileLength; ++tileIndex) {
  378. addDrawCommandsForTile(this, tilesToRender[tileIndex], frameState);
  379. }
  380. }
  381. };
  382. /**
  383. * Adds draw commands for tiles rendered in the previous frame for a pick pass.
  384. *
  385. * @param {FrameState} frameState The frame state.
  386. */
  387. GlobeSurfaceTileProvider.prototype.updateForPick = function(frameState) {
  388. // Add the tile pick commands from the tiles drawn last frame.
  389. var drawCommands = this._drawCommands;
  390. for (var i = 0, length = this._usedDrawCommands; i < length; ++i) {
  391. frameState.commandList.push(drawCommands[i]);
  392. }
  393. };
  394. /**
  395. * Cancels any imagery re-projections in the queue.
  396. */
  397. GlobeSurfaceTileProvider.prototype.cancelReprojections = function() {
  398. this._imageryLayers.cancelReprojections();
  399. };
  400. /**
  401. * Gets the maximum geometric error allowed in a tile at a given level, in meters. This function should not be
  402. * called before {@link GlobeSurfaceTileProvider#ready} returns true.
  403. *
  404. * @param {Number} level The tile level for which to get the maximum geometric error.
  405. * @returns {Number} The maximum geometric error in meters.
  406. */
  407. GlobeSurfaceTileProvider.prototype.getLevelMaximumGeometricError = function(level) {
  408. return this._terrainProvider.getLevelMaximumGeometricError(level);
  409. };
  410. /**
  411. * Loads, or continues loading, a given tile. This function will continue to be called
  412. * until {@link QuadtreeTile#state} is no longer {@link QuadtreeTileLoadState#LOADING}. This function should
  413. * not be called before {@link GlobeSurfaceTileProvider#ready} returns true.
  414. *
  415. * @param {FrameState} frameState The frame state.
  416. * @param {QuadtreeTile} tile The tile to load.
  417. *
  418. * @exception {DeveloperError} <code>loadTile</code> must not be called before the tile provider is ready.
  419. */
  420. GlobeSurfaceTileProvider.prototype.loadTile = function(frameState, tile) {
  421. // We don't want to load imagery until we're certain that the terrain tiles are actually visible.
  422. // So if our bounding volume isn't accurate because it came from another tile, load terrain only
  423. // initially. If we load some terrain and suddenly have a more accurate bounding volume and the
  424. // tile is _still_ visible, give the tile a chance to load imagery immediately rather than
  425. // waiting for next frame.
  426. var surfaceTile = tile.data;
  427. var terrainOnly = true;
  428. var terrainStateBefore;
  429. if (defined(surfaceTile)) {
  430. terrainOnly = surfaceTile.boundingVolumeSourceTile !== tile || tile._lastSelectionResult === TileSelectionResult.CULLED_BUT_NEEDED;
  431. terrainStateBefore = surfaceTile.terrainState;
  432. }
  433. GlobeSurfaceTile.processStateMachine(tile, frameState, this.terrainProvider, this._imageryLayers, this._vertexArraysToDestroy, terrainOnly);
  434. surfaceTile = tile.data;
  435. if (terrainOnly && terrainStateBefore !== tile.data.terrainState) {
  436. // Terrain state changed. If:
  437. // a) The tile is visible, and
  438. // b) The bounding volume is accurate (updated as a side effect of computing visibility)
  439. // Then we'll load imagery, too.
  440. if (this.computeTileVisibility(tile, frameState, this.quadtree.occluders) && surfaceTile.boundingVolumeSourceTile === tile) {
  441. terrainOnly = false;
  442. GlobeSurfaceTile.processStateMachine(tile, frameState, this.terrainProvider, this._imageryLayers, this._vertexArraysToDestroy, terrainOnly);
  443. }
  444. }
  445. };
  446. var boundingSphereScratch = new BoundingSphere();
  447. var rectangleIntersectionScratch = new Rectangle();
  448. var splitCartographicLimitRectangleScratch = new Rectangle();
  449. var rectangleCenterScratch = new Cartographic();
  450. // cartographicLimitRectangle may span the IDL, but tiles never will.
  451. function clipRectangleAntimeridian(tileRectangle, cartographicLimitRectangle) {
  452. if (cartographicLimitRectangle.west < cartographicLimitRectangle.east) {
  453. return cartographicLimitRectangle;
  454. }
  455. var splitRectangle = Rectangle.clone(cartographicLimitRectangle, splitCartographicLimitRectangleScratch);
  456. var tileCenter = Rectangle.center(tileRectangle, rectangleCenterScratch);
  457. if (tileCenter.longitude > 0.0) {
  458. splitRectangle.east = CesiumMath.PI;
  459. } else {
  460. splitRectangle.west = -CesiumMath.PI;
  461. }
  462. return splitRectangle;
  463. }
  464. /**
  465. * Determines the visibility of a given tile. The tile may be fully visible, partially visible, or not
  466. * visible at all. Tiles that are renderable and are at least partially visible will be shown by a call
  467. * to {@link GlobeSurfaceTileProvider#showTileThisFrame}.
  468. *
  469. * @param {QuadtreeTile} tile The tile instance.
  470. * @param {FrameState} frameState The state information about the current frame.
  471. * @param {QuadtreeOccluders} occluders The objects that may occlude this tile.
  472. *
  473. * @returns {Visibility} The visibility of the tile.
  474. */
  475. GlobeSurfaceTileProvider.prototype.computeTileVisibility = function(tile, frameState, occluders) {
  476. var distance = this.computeDistanceToTile(tile, frameState);
  477. tile._distance = distance;
  478. if (frameState.fog.enabled) {
  479. if (CesiumMath.fog(distance, frameState.fog.density) >= 1.0) {
  480. // Tile is completely in fog so return that it is not visible.
  481. return Visibility.NONE;
  482. }
  483. }
  484. var surfaceTile = tile.data;
  485. var tileBoundingRegion = surfaceTile.tileBoundingRegion;
  486. if (surfaceTile.boundingVolumeSourceTile === undefined) {
  487. // We have no idea where this tile is, so let's just call it partially visible.
  488. return Visibility.PARTIAL;
  489. }
  490. var cullingVolume = frameState.cullingVolume;
  491. var boundingVolume = surfaceTile.orientedBoundingBox;
  492. if (!defined(boundingVolume) && defined(surfaceTile.renderedMesh)) {
  493. boundingVolume = surfaceTile.renderedMesh.boundingSphere3D;
  494. }
  495. // Check if the tile is outside the limit area in cartographic space
  496. surfaceTile.clippedByBoundaries = false;
  497. var clippedCartographicLimitRectangle = clipRectangleAntimeridian(tile.rectangle, this.cartographicLimitRectangle);
  498. var areaLimitIntersection = Rectangle.simpleIntersection(clippedCartographicLimitRectangle, tile.rectangle, rectangleIntersectionScratch);
  499. if (!defined(areaLimitIntersection)) {
  500. return Visibility.NONE;
  501. }
  502. if (!Rectangle.equals(areaLimitIntersection, tile.rectangle)) {
  503. surfaceTile.clippedByBoundaries = true;
  504. }
  505. if (frameState.mode !== SceneMode.SCENE3D) {
  506. boundingVolume = boundingSphereScratch;
  507. BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, tileBoundingRegion.minimumHeight, tileBoundingRegion.maximumHeight, boundingVolume);
  508. Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center);
  509. if (frameState.mode === SceneMode.MORPHING && defined(surfaceTile.renderedMesh)) {
  510. boundingVolume = BoundingSphere.union(surfaceTile.renderedMesh.boundingSphere3D, boundingVolume, boundingVolume);
  511. }
  512. }
  513. if (!defined(boundingVolume)) {
  514. return Intersect.INTERSECTING;
  515. }
  516. var clippingPlanes = this._clippingPlanes;
  517. if (defined(clippingPlanes) && clippingPlanes.enabled) {
  518. var planeIntersection = clippingPlanes.computeIntersectionWithBoundingVolume(boundingVolume);
  519. tile.isClipped = (planeIntersection !== Intersect.INSIDE);
  520. if (planeIntersection === Intersect.OUTSIDE) {
  521. return Visibility.NONE;
  522. }
  523. }
  524. var intersection = cullingVolume.computeVisibility(boundingVolume);
  525. if (intersection === Intersect.OUTSIDE) {
  526. return Visibility.NONE;
  527. }
  528. var ortho3D = frameState.mode === SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum;
  529. if (frameState.mode === SceneMode.SCENE3D && !ortho3D && defined(occluders)) {
  530. var occludeePointInScaledSpace = surfaceTile.occludeePointInScaledSpace;
  531. if (!defined(occludeePointInScaledSpace)) {
  532. return intersection;
  533. }
  534. if (occluders.ellipsoid.isScaledSpacePointVisible(occludeePointInScaledSpace)) {
  535. return intersection;
  536. }
  537. return Visibility.NONE;
  538. }
  539. return intersection;
  540. };
  541. /**
  542. * Determines if the given tile can be refined
  543. * @param {QuadtreeTile} tile The tile to check.
  544. * @returns {boolean} True if the tile can be refined, false if it cannot.
  545. */
  546. GlobeSurfaceTileProvider.prototype.canRefine = function(tile) {
  547. // Only allow refinement it we know whether or not the children of this tile exist.
  548. // For a tileset with `availability`, we'll always be able to refine.
  549. // We can ask for availability of _any_ child tile because we only need to confirm
  550. // that we get a yes or no answer, it doesn't matter what the answer is.
  551. if (defined(tile.data.terrainData)) {
  552. return true;
  553. }
  554. var childAvailable = this.terrainProvider.getTileDataAvailable(tile.x * 2, tile.y * 2, tile.level + 1);
  555. return childAvailable !== undefined;
  556. };
  557. var readyImageryScratch = [];
  558. var canRenderTraversalStack = [];
  559. /**
  560. * Determines if the given not-fully-loaded tile can be rendered without losing detail that
  561. * was present last frame as a result of rendering descendant tiles. This method will only be
  562. * called if this tile's descendants were rendered last frame. If the tile is fully loaded,
  563. * it is assumed that this method will return true and it will not be called.
  564. * @param {QuadtreeTile} tile The tile to check.
  565. * @returns {boolean} True if the tile can be rendered without losing detail.
  566. */
  567. GlobeSurfaceTileProvider.prototype.canRenderWithoutLosingDetail = function(tile, frameState) {
  568. var surfaceTile = tile.data;
  569. var readyImagery = readyImageryScratch;
  570. readyImagery.length = this._imageryLayers.length;
  571. var terrainReady = false;
  572. var initialImageryState = false;
  573. var imagery;
  574. if (defined(surfaceTile)) {
  575. // We can render even with non-ready terrain as long as all our rendered descendants
  576. // are missing terrain geometry too. i.e. if we rendered fills for more detailed tiles
  577. // last frame, it's ok to render a fill for this tile this frame.
  578. terrainReady = surfaceTile.terrainState === TerrainState.READY;
  579. // Initially assume all imagery layers are ready, unless imagery hasn't been initialized at all.
  580. initialImageryState = true;
  581. imagery = surfaceTile.imagery;
  582. }
  583. var i;
  584. var len;
  585. for (i = 0, len = readyImagery.length; i < len; ++i) {
  586. readyImagery[i] = initialImageryState;
  587. }
  588. if (defined(imagery)) {
  589. for (i = 0, len = imagery.length; i < len; ++i) {
  590. var tileImagery = imagery[i];
  591. var loadingImagery = tileImagery.loadingImagery;
  592. var isReady = !defined(loadingImagery) || loadingImagery.state === ImageryState.FAILED || loadingImagery.state === ImageryState.INVALID;
  593. var layerIndex = (tileImagery.loadingImagery || tileImagery.readyImagery).imageryLayer._layerIndex;
  594. // For a layer to be ready, all tiles belonging to that layer must be ready.
  595. readyImagery[layerIndex] = isReady && readyImagery[layerIndex];
  596. }
  597. }
  598. var lastFrame = this.quadtree._lastSelectionFrameNumber;
  599. // Traverse the descendants looking for one with terrain or imagery that is not loaded on this tile.
  600. var stack = canRenderTraversalStack;
  601. stack.length = 0;
  602. stack.push(tile.southwestChild, tile.southeastChild, tile.northwestChild, tile.northeastChild);
  603. while (stack.length > 0) {
  604. var descendant = stack.pop();
  605. var lastFrameSelectionResult = descendant._lastSelectionResultFrame === lastFrame ? descendant._lastSelectionResult : TileSelectionResult.NONE;
  606. if (lastFrameSelectionResult === TileSelectionResult.RENDERED) {
  607. var descendantSurface = descendant.data;
  608. if (!defined(descendantSurface)) {
  609. // Descendant has no data, so it can't block rendering.
  610. continue;
  611. }
  612. if (!terrainReady && descendant.data.terrainState === TerrainState.READY) {
  613. // Rendered descendant has real terrain, but we don't. Rendering is blocked.
  614. return false;
  615. }
  616. var descendantImagery = descendant.data.imagery;
  617. for (i = 0, len = descendantImagery.length; i < len; ++i) {
  618. var descendantTileImagery = descendantImagery[i];
  619. var descendantLoadingImagery = descendantTileImagery.loadingImagery;
  620. var descendantIsReady = !defined(descendantLoadingImagery) || descendantLoadingImagery.state === ImageryState.FAILED || descendantLoadingImagery.state === ImageryState.INVALID;
  621. var descendantLayerIndex = (descendantTileImagery.loadingImagery || descendantTileImagery.readyImagery).imageryLayer._layerIndex;
  622. // If this imagery tile of a descendant is ready but the layer isn't ready in this tile,
  623. // then rendering is blocked.
  624. if (descendantIsReady && !readyImagery[descendantLayerIndex]) {
  625. return false;
  626. }
  627. }
  628. } else if (lastFrameSelectionResult === TileSelectionResult.REFINED) {
  629. stack.push(descendant.southwestChild, descendant.southeastChild, descendant.northwestChild, descendant.northeastChild);
  630. }
  631. }
  632. return true;
  633. };
  634. var tileDirectionScratch = new Cartesian3();
  635. /**
  636. * Determines the priority for loading this tile. Lower priority values load sooner.
  637. * @param {QuadtreeTile} tile The tile.
  638. * @param {FrameState} frameState The frame state.
  639. * @returns {Number} The load priority value.
  640. */
  641. GlobeSurfaceTileProvider.prototype.computeTileLoadPriority = function(tile, frameState) {
  642. var surfaceTile = tile.data;
  643. if (surfaceTile === undefined) {
  644. return 0.0;
  645. }
  646. var obb = surfaceTile.orientedBoundingBox;
  647. if (obb === undefined) {
  648. return 0.0;
  649. }
  650. var cameraPosition = frameState.camera.positionWC;
  651. var cameraDirection = frameState.camera.directionWC;
  652. var tileDirection = Cartesian3.subtract(obb.center, cameraPosition, tileDirectionScratch);
  653. var magnitude = Cartesian3.magnitude(tileDirection);
  654. if (magnitude < CesiumMath.EPSILON5) {
  655. return 0.0;
  656. }
  657. Cartesian3.divideByScalar(tileDirection, magnitude, tileDirection);
  658. return (1.0 - Cartesian3.dot(tileDirection, cameraDirection)) * tile._distance;
  659. };
  660. var modifiedModelViewScratch = new Matrix4();
  661. var modifiedModelViewProjectionScratch = new Matrix4();
  662. var tileRectangleScratch = new Cartesian4();
  663. var localizedCartographicLimitRectangleScratch = new Cartesian4();
  664. var rtcScratch = new Cartesian3();
  665. var centerEyeScratch = new Cartesian3();
  666. var southwestScratch = new Cartesian3();
  667. var northeastScratch = new Cartesian3();
  668. /**
  669. * Shows a specified tile in this frame. The provider can cause the tile to be shown by adding
  670. * render commands to the commandList, or use any other method as appropriate. The tile is not
  671. * expected to be visible next frame as well, unless this method is called next frame, too.
  672. *
  673. * @param {QuadtreeTile} tile The tile instance.
  674. * @param {FrameState} frameState The state information of the current rendering frame.
  675. */
  676. GlobeSurfaceTileProvider.prototype.showTileThisFrame = function(tile, frameState) {
  677. var readyTextureCount = 0;
  678. var tileImageryCollection = tile.data.imagery;
  679. for (var i = 0, len = tileImageryCollection.length; i < len; ++i) {
  680. var tileImagery = tileImageryCollection[i];
  681. if (defined(tileImagery.readyImagery) && tileImagery.readyImagery.imageryLayer.alpha !== 0.0) {
  682. ++readyTextureCount;
  683. }
  684. }
  685. var tileSet = this._tilesToRenderByTextureCount[readyTextureCount];
  686. if (!defined(tileSet)) {
  687. tileSet = [];
  688. this._tilesToRenderByTextureCount[readyTextureCount] = tileSet;
  689. }
  690. tileSet.push(tile);
  691. var surfaceTile = tile.data;
  692. if (!defined(surfaceTile.vertexArray)) {
  693. this._hasFillTilesThisFrame = true;
  694. } else {
  695. this._hasLoadedTilesThisFrame = true;
  696. }
  697. var debug = this._debug;
  698. ++debug.tilesRendered;
  699. debug.texturesRendered += readyTextureCount;
  700. };
  701. var cornerPositionsScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
  702. function computeOccludeePoint(tileProvider, center, rectangle, height, result) {
  703. var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
  704. var ellipsoid = ellipsoidalOccluder.ellipsoid;
  705. var cornerPositions = cornerPositionsScratch;
  706. Cartesian3.fromRadians(rectangle.west, rectangle.south, height, ellipsoid, cornerPositions[0]);
  707. Cartesian3.fromRadians(rectangle.east, rectangle.south, height, ellipsoid, cornerPositions[1]);
  708. Cartesian3.fromRadians(rectangle.west, rectangle.north, height, ellipsoid, cornerPositions[2]);
  709. Cartesian3.fromRadians(rectangle.east, rectangle.north, height, ellipsoid, cornerPositions[3]);
  710. return ellipsoidalOccluder.computeHorizonCullingPoint(center, cornerPositions, result);
  711. }
  712. /**
  713. * Gets the distance from the camera to the closest point on the tile. This is used for level-of-detail selection.
  714. *
  715. * @param {QuadtreeTile} tile The tile instance.
  716. * @param {FrameState} frameState The state information of the current rendering frame.
  717. *
  718. * @returns {Number} The distance from the camera to the closest point on the tile, in meters.
  719. */
  720. GlobeSurfaceTileProvider.prototype.computeDistanceToTile = function(tile, frameState) {
  721. // The distance should be:
  722. // 1. the actual distance to the tight-fitting bounding volume, or
  723. // 2. a distance that is equal to or greater than the actual distance to the tight-fitting bounding volume.
  724. //
  725. // When we don't know the min/max heights for a tile, but we do know the min/max of an ancestor tile, we can
  726. // build a tight-fitting bounding volume horizontally, but not vertically. The min/max heights from the
  727. // ancestor will likely form a volume that is much bigger than it needs to be. This means that the volume may
  728. // be deemed to be much closer to the camera than it really is, causing us to select tiles that are too detailed.
  729. // Loading too-detailed tiles is super expensive, so we don't want to do that. We don't know where the child
  730. // tile really lies within the parent range of heights, but we _do_ know the child tile can't be any closer than
  731. // the ancestor height surface (min or max) that is _farthest away_ from the camera. So if we compute distance
  732. // based that conservative metric, we may end up loading tiles that are not detailed enough, but that's much
  733. // better (faster) than loading tiles that are too detailed.
  734. var heightSource = updateTileBoundingRegion(tile, this.terrainProvider, frameState);
  735. var surfaceTile = tile.data;
  736. var tileBoundingRegion = surfaceTile.tileBoundingRegion;
  737. if (heightSource === undefined) {
  738. // Can't find any min/max heights anywhere? Ok, let's just say the
  739. // tile is really far away so we'll load and render it rather than
  740. // refining.
  741. return 9999999999.0;
  742. } else if (surfaceTile.boundingVolumeSourceTile !== heightSource) {
  743. // Heights are from a new source tile, so update the bounding volume.
  744. surfaceTile.boundingVolumeSourceTile = heightSource;
  745. var rectangle = tile.rectangle;
  746. if (defined(rectangle) && rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
  747. surfaceTile.orientedBoundingBox = OrientedBoundingBox.fromRectangle(
  748. tile.rectangle,
  749. tileBoundingRegion.minimumHeight,
  750. tileBoundingRegion.maximumHeight,
  751. tile.tilingScheme.ellipsoid,
  752. surfaceTile.orientedBoundingBox);
  753. surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace);
  754. }
  755. }
  756. var min = tileBoundingRegion.minimumHeight;
  757. var max = tileBoundingRegion.maximumHeight;
  758. if (surfaceTile.boundingVolumeSourceTile !== tile) {
  759. var cameraHeight = frameState.camera.positionCartographic.height;
  760. var distanceToMin = Math.abs(cameraHeight - min);
  761. var distanceToMax = Math.abs(cameraHeight - max);
  762. if (distanceToMin > distanceToMax) {
  763. tileBoundingRegion.minimumHeight = min;
  764. tileBoundingRegion.maximumHeight = min;
  765. } else {
  766. tileBoundingRegion.minimumHeight = max;
  767. tileBoundingRegion.maximumHeight = max;
  768. }
  769. }
  770. var result = tileBoundingRegion.distanceToCamera(frameState);
  771. tileBoundingRegion.minimumHeight = min;
  772. tileBoundingRegion.maximumHeight = max;
  773. return result;
  774. };
  775. function updateTileBoundingRegion(tile, terrainProvider, frameState) {
  776. var surfaceTile = tile.data;
  777. if (surfaceTile === undefined) {
  778. surfaceTile = tile.data = new GlobeSurfaceTile();
  779. }
  780. if (surfaceTile.tileBoundingRegion === undefined) {
  781. surfaceTile.tileBoundingRegion = new TileBoundingRegion({
  782. computeBoundingVolumes : false,
  783. rectangle : tile.rectangle,
  784. ellipsoid : tile.tilingScheme.ellipsoid,
  785. minimumHeight : 0.0,
  786. maximumHeight : 0.0
  787. });
  788. }
  789. var terrainData = surfaceTile.terrainData;
  790. var mesh = surfaceTile.mesh;
  791. var tileBoundingRegion = surfaceTile.tileBoundingRegion;
  792. if (mesh !== undefined && mesh.minimumHeight !== undefined && mesh.maximumHeight !== undefined) {
  793. // We have tight-fitting min/max heights from the mesh.
  794. tileBoundingRegion.minimumHeight = mesh.minimumHeight;
  795. tileBoundingRegion.maximumHeight = mesh.maximumHeight;
  796. return tile;
  797. }
  798. if (terrainData !== undefined && terrainData._minimumHeight !== undefined && terrainData._maximumHeight !== undefined) {
  799. // We have tight-fitting min/max heights from the terrain data.
  800. tileBoundingRegion.minimumHeight = terrainData._minimumHeight * frameState.terrainExaggeration;
  801. tileBoundingRegion.maximumHeight = terrainData._maximumHeight * frameState.terrainExaggeration;
  802. return tile;
  803. }
  804. // No accurate min/max heights available, so we're stuck with min/max heights from an ancestor tile.
  805. tileBoundingRegion.minimumHeight = Number.NaN;
  806. tileBoundingRegion.maximumHeight = Number.NaN;
  807. var ancestor = tile.parent;
  808. while (ancestor !== undefined) {
  809. var ancestorSurfaceTile = ancestor.data;
  810. if (ancestorSurfaceTile !== undefined) {
  811. var ancestorMesh = ancestorSurfaceTile.mesh;
  812. if (ancestorMesh !== undefined && ancestorMesh.minimumHeight !== undefined && ancestorMesh.maximumHeight !== undefined) {
  813. tileBoundingRegion.minimumHeight = ancestorMesh.minimumHeight;
  814. tileBoundingRegion.maximumHeight = ancestorMesh.maximumHeight;
  815. return ancestor;
  816. }
  817. var ancestorTerrainData = ancestorSurfaceTile.terrainData;
  818. if (ancestorTerrainData !== undefined && ancestorTerrainData._minimumHeight !== undefined && ancestorTerrainData._maximumHeight !== undefined) {
  819. tileBoundingRegion.minimumHeight = ancestorTerrainData._minimumHeight * frameState.terrainExaggeration;
  820. tileBoundingRegion.maximumHeight = ancestorTerrainData._maximumHeight * frameState.terrainExaggeration;
  821. return ancestor;
  822. }
  823. }
  824. ancestor = ancestor.parent;
  825. }
  826. return undefined;
  827. }
  828. /**
  829. * Returns true if this object was destroyed; otherwise, false.
  830. * <br /><br />
  831. * If this object was destroyed, it should not be used; calling any function other than
  832. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  833. *
  834. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  835. *
  836. * @see GlobeSurfaceTileProvider#destroy
  837. */
  838. GlobeSurfaceTileProvider.prototype.isDestroyed = function() {
  839. return false;
  840. };
  841. /**
  842. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  843. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  844. * <br /><br />
  845. * Once an object is destroyed, it should not be used; calling any function other than
  846. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  847. * assign the return value (<code>undefined</code>) to the object as done in the example.
  848. *
  849. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  850. *
  851. *
  852. * @example
  853. * provider = provider && provider();
  854. *
  855. * @see GlobeSurfaceTileProvider#isDestroyed
  856. */
  857. GlobeSurfaceTileProvider.prototype.destroy = function() {
  858. this._tileProvider = this._tileProvider && this._tileProvider.destroy();
  859. this._clippingPlanes = this._clippingPlanes && this._clippingPlanes.destroy();
  860. return destroyObject(this);
  861. };
  862. function getTileReadyCallback(tileImageriesToFree, layer, terrainProvider) {
  863. return function(tile) {
  864. var tileImagery;
  865. var imagery;
  866. var startIndex = -1;
  867. var tileImageryCollection = tile.data.imagery;
  868. var length = tileImageryCollection.length;
  869. var i;
  870. for (i = 0; i < length; ++i) {
  871. tileImagery = tileImageryCollection[i];
  872. imagery = defaultValue(tileImagery.readyImagery, tileImagery.loadingImagery);
  873. if (imagery.imageryLayer === layer) {
  874. startIndex = i;
  875. break;
  876. }
  877. }
  878. if (startIndex !== -1) {
  879. var endIndex = startIndex + tileImageriesToFree;
  880. tileImagery = tileImageryCollection[endIndex];
  881. imagery = defined(tileImagery) ? defaultValue(tileImagery.readyImagery, tileImagery.loadingImagery) : undefined;
  882. if (!defined(imagery) || imagery.imageryLayer !== layer) {
  883. // Return false to keep the callback if we have to wait on the skeletons
  884. // Return true to remove the callback if something went wrong
  885. return !(layer._createTileImagerySkeletons(tile, terrainProvider, endIndex));
  886. }
  887. for (i = startIndex; i < endIndex; ++i) {
  888. tileImageryCollection[i].freeResources();
  889. }
  890. tileImageryCollection.splice(startIndex, tileImageriesToFree);
  891. }
  892. return true; // Everything is done, so remove the callback
  893. };
  894. }
  895. GlobeSurfaceTileProvider.prototype._onLayerAdded = function(layer, index) {
  896. if (layer.show) {
  897. var terrainProvider = this._terrainProvider;
  898. var that = this;
  899. var imageryProvider = layer.imageryProvider;
  900. var tileImageryUpdatedEvent = this._imageryLayersUpdatedEvent;
  901. imageryProvider._reload = function() {
  902. // Clear the layer's cache
  903. layer._imageryCache = {};
  904. that._quadtree.forEachLoadedTile(function(tile) {
  905. // If this layer is still waiting to for the loaded callback, just return
  906. if (defined(tile._loadedCallbacks[layer._layerIndex])) {
  907. return;
  908. }
  909. var i;
  910. // Figure out how many TileImageries we will need to remove and where to insert new ones
  911. var tileImageryCollection = tile.data.imagery;
  912. var length = tileImageryCollection.length;
  913. var startIndex = -1;
  914. var tileImageriesToFree = 0;
  915. for (i = 0; i < length; ++i) {
  916. var tileImagery = tileImageryCollection[i];
  917. var imagery = defaultValue(tileImagery.readyImagery, tileImagery.loadingImagery);
  918. if (imagery.imageryLayer === layer) {
  919. if (startIndex === -1) {
  920. startIndex = i;
  921. }
  922. ++tileImageriesToFree;
  923. } else if (startIndex !== -1) {
  924. // iterated past the section of TileImageries belonging to this layer, no need to continue.
  925. break;
  926. }
  927. }
  928. if (startIndex === -1) {
  929. return;
  930. }
  931. // Insert immediately after existing TileImageries
  932. var insertionPoint = startIndex + tileImageriesToFree;
  933. // Create new TileImageries for all loaded tiles
  934. if (layer._createTileImagerySkeletons(tile, terrainProvider, insertionPoint)) {
  935. // Add callback to remove old TileImageries when the new TileImageries are ready
  936. tile._loadedCallbacks[layer._layerIndex] = getTileReadyCallback(tileImageriesToFree, layer, terrainProvider);
  937. tile.state = QuadtreeTileLoadState.LOADING;
  938. }
  939. });
  940. };
  941. // create TileImageries for this layer for all previously loaded tiles
  942. this._quadtree.forEachLoadedTile(function(tile) {
  943. if (layer._createTileImagerySkeletons(tile, terrainProvider)) {
  944. tile.state = QuadtreeTileLoadState.LOADING;
  945. // Tiles that are not currently being rendered need to load the new layer before they're renderable.
  946. // We don't mark the rendered tiles non-renderable, though, because that would make the globe disappear.
  947. if (tile.level !== 0 && (tile._lastSelectionResultFrame !== that.quadtree._lastSelectionFrameNumber || tile._lastSelectionResult !== TileSelectionResult.RENDERED)) {
  948. tile.renderable = false;
  949. }
  950. }
  951. });
  952. this._layerOrderChanged = true;
  953. tileImageryUpdatedEvent.raiseEvent();
  954. }
  955. };
  956. GlobeSurfaceTileProvider.prototype._onLayerRemoved = function(layer, index) {
  957. // destroy TileImagerys for this layer for all previously loaded tiles
  958. this._quadtree.forEachLoadedTile(function(tile) {
  959. var tileImageryCollection = tile.data.imagery;
  960. var startIndex = -1;
  961. var numDestroyed = 0;
  962. for (var i = 0, len = tileImageryCollection.length; i < len; ++i) {
  963. var tileImagery = tileImageryCollection[i];
  964. var imagery = tileImagery.loadingImagery;
  965. if (!defined(imagery)) {
  966. imagery = tileImagery.readyImagery;
  967. }
  968. if (imagery.imageryLayer === layer) {
  969. if (startIndex === -1) {
  970. startIndex = i;
  971. }
  972. tileImagery.freeResources();
  973. ++numDestroyed;
  974. } else if (startIndex !== -1) {
  975. // iterated past the section of TileImagerys belonging to this layer, no need to continue.
  976. break;
  977. }
  978. }
  979. if (startIndex !== -1) {
  980. tileImageryCollection.splice(startIndex, numDestroyed);
  981. }
  982. });
  983. if (defined(layer.imageryProvider)) {
  984. layer.imageryProvider._reload = undefined;
  985. }
  986. this._imageryLayersUpdatedEvent.raiseEvent();
  987. };
  988. GlobeSurfaceTileProvider.prototype._onLayerMoved = function(layer, newIndex, oldIndex) {
  989. this._layerOrderChanged = true;
  990. this._imageryLayersUpdatedEvent.raiseEvent();
  991. };
  992. GlobeSurfaceTileProvider.prototype._onLayerShownOrHidden = function(layer, index, show) {
  993. if (show) {
  994. this._onLayerAdded(layer, index);
  995. } else {
  996. this._onLayerRemoved(layer, index);
  997. }
  998. };
  999. var scratchClippingPlaneMatrix = new Matrix4();
  1000. function createTileUniformMap(frameState, globeSurfaceTileProvider) {
  1001. var uniformMap = {
  1002. u_initialColor : function() {
  1003. return this.properties.initialColor;
  1004. },
  1005. u_fillHighlightColor : function() {
  1006. return this.properties.fillHighlightColor;
  1007. },
  1008. u_zoomedOutOceanSpecularIntensity : function() {
  1009. return this.properties.zoomedOutOceanSpecularIntensity;
  1010. },
  1011. u_oceanNormalMap : function() {
  1012. return this.properties.oceanNormalMap;
  1013. },
  1014. u_lightingFadeDistance : function() {
  1015. return this.properties.lightingFadeDistance;
  1016. },
  1017. u_nightFadeDistance : function() {
  1018. return this.properties.nightFadeDistance;
  1019. },
  1020. u_center3D : function() {
  1021. return this.properties.center3D;
  1022. },
  1023. u_tileRectangle : function() {
  1024. return this.properties.tileRectangle;
  1025. },
  1026. u_modifiedModelView : function() {
  1027. var viewMatrix = frameState.context.uniformState.view;
  1028. var centerEye = Matrix4.multiplyByPoint(viewMatrix, this.properties.rtc, centerEyeScratch);
  1029. Matrix4.setTranslation(viewMatrix, centerEye, modifiedModelViewScratch);
  1030. return modifiedModelViewScratch;
  1031. },
  1032. u_modifiedModelViewProjection : function() {
  1033. var viewMatrix = frameState.context.uniformState.view;
  1034. var projectionMatrix = frameState.context.uniformState.projection;
  1035. var centerEye = Matrix4.multiplyByPoint(viewMatrix, this.properties.rtc, centerEyeScratch);
  1036. Matrix4.setTranslation(viewMatrix, centerEye, modifiedModelViewProjectionScratch);
  1037. Matrix4.multiply(projectionMatrix, modifiedModelViewProjectionScratch, modifiedModelViewProjectionScratch);
  1038. return modifiedModelViewProjectionScratch;
  1039. },
  1040. u_dayTextures : function() {
  1041. return this.properties.dayTextures;
  1042. },
  1043. u_dayTextureTranslationAndScale : function() {
  1044. return this.properties.dayTextureTranslationAndScale;
  1045. },
  1046. u_dayTextureTexCoordsRectangle : function() {
  1047. return this.properties.dayTextureTexCoordsRectangle;
  1048. },
  1049. u_dayTextureUseWebMercatorT : function() {
  1050. return this.properties.dayTextureUseWebMercatorT;
  1051. },
  1052. u_dayTextureAlpha : function() {
  1053. return this.properties.dayTextureAlpha;
  1054. },
  1055. u_dayTextureBrightness : function() {
  1056. return this.properties.dayTextureBrightness;
  1057. },
  1058. u_dayTextureContrast : function() {
  1059. return this.properties.dayTextureContrast;
  1060. },
  1061. u_dayTextureHue : function() {
  1062. return this.properties.dayTextureHue;
  1063. },
  1064. u_dayTextureSaturation : function() {
  1065. return this.properties.dayTextureSaturation;
  1066. },
  1067. u_dayTextureOneOverGamma : function() {
  1068. return this.properties.dayTextureOneOverGamma;
  1069. },
  1070. u_dayIntensity : function() {
  1071. return this.properties.dayIntensity;
  1072. },
  1073. u_southAndNorthLatitude : function() {
  1074. return this.properties.southAndNorthLatitude;
  1075. },
  1076. u_southMercatorYAndOneOverHeight : function() {
  1077. return this.properties.southMercatorYAndOneOverHeight;
  1078. },
  1079. u_waterMask : function() {
  1080. return this.properties.waterMask;
  1081. },
  1082. u_waterMaskTranslationAndScale : function() {
  1083. return this.properties.waterMaskTranslationAndScale;
  1084. },
  1085. u_minMaxHeight : function() {
  1086. return this.properties.minMaxHeight;
  1087. },
  1088. u_scaleAndBias : function() {
  1089. return this.properties.scaleAndBias;
  1090. },
  1091. u_dayTextureSplit : function() {
  1092. return this.properties.dayTextureSplit;
  1093. },
  1094. u_dayTextureCutoutRectangles : function() {
  1095. return this.properties.dayTextureCutoutRectangles;
  1096. },
  1097. u_clippingPlanes : function() {
  1098. var clippingPlanes = globeSurfaceTileProvider._clippingPlanes;
  1099. if (defined(clippingPlanes) && defined(clippingPlanes.texture)) {
  1100. // Check in case clippingPlanes hasn't been updated yet.
  1101. return clippingPlanes.texture;
  1102. }
  1103. return frameState.context.defaultTexture;
  1104. },
  1105. u_cartographicLimitRectangle : function() {
  1106. return this.properties.localizedCartographicLimitRectangle;
  1107. },
  1108. u_clippingPlanesMatrix : function() {
  1109. var clippingPlanes = globeSurfaceTileProvider._clippingPlanes;
  1110. return defined(clippingPlanes) ? Matrix4.multiply(frameState.context.uniformState.view, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix) : Matrix4.IDENTITY;
  1111. },
  1112. u_clippingPlanesEdgeStyle : function() {
  1113. var style = this.properties.clippingPlanesEdgeColor;
  1114. style.alpha = this.properties.clippingPlanesEdgeWidth;
  1115. return style;
  1116. },
  1117. u_minimumBrightness : function() {
  1118. return frameState.fog.minimumBrightness;
  1119. },
  1120. u_hsbShift : function() {
  1121. return this.properties.hsbShift;
  1122. },
  1123. u_colorsToAlpha : function() {
  1124. return this.properties.colorsToAlpha;
  1125. },
  1126. // make a separate object so that changes to the properties are seen on
  1127. // derived commands that combine another uniform map with this one.
  1128. properties : {
  1129. initialColor : new Cartesian4(0.0, 0.0, 0.5, 1.0),
  1130. fillHighlightColor : new Color(0.0, 0.0, 0.0, 0.0),
  1131. zoomedOutOceanSpecularIntensity : 0.5,
  1132. oceanNormalMap : undefined,
  1133. lightingFadeDistance : new Cartesian2(6500000.0, 9000000.0),
  1134. nightFadeDistance : new Cartesian2(10000000.0, 40000000.0),
  1135. hsbShift : new Cartesian3(),
  1136. center3D : undefined,
  1137. rtc : new Cartesian3(),
  1138. modifiedModelView : new Matrix4(),
  1139. tileRectangle : new Cartesian4(),
  1140. dayTextures : [],
  1141. dayTextureTranslationAndScale : [],
  1142. dayTextureTexCoordsRectangle : [],
  1143. dayTextureUseWebMercatorT : [],
  1144. dayTextureAlpha : [],
  1145. dayTextureBrightness : [],
  1146. dayTextureContrast : [],
  1147. dayTextureHue : [],
  1148. dayTextureSaturation : [],
  1149. dayTextureOneOverGamma : [],
  1150. dayTextureSplit : [],
  1151. dayTextureCutoutRectangles : [],
  1152. dayIntensity : 0.0,
  1153. colorsToAlpha : [],
  1154. southAndNorthLatitude : new Cartesian2(),
  1155. southMercatorYAndOneOverHeight : new Cartesian2(),
  1156. waterMask : undefined,
  1157. waterMaskTranslationAndScale : new Cartesian4(),
  1158. minMaxHeight : new Cartesian2(),
  1159. scaleAndBias : new Matrix4(),
  1160. clippingPlanesEdgeColor : Color.clone(Color.WHITE),
  1161. clippingPlanesEdgeWidth : 0.0,
  1162. localizedCartographicLimitRectangle : new Cartesian4()
  1163. }
  1164. };
  1165. return uniformMap;
  1166. }
  1167. function createWireframeVertexArrayIfNecessary(context, provider, tile) {
  1168. var surfaceTile = tile.data;
  1169. var mesh;
  1170. var vertexArray;
  1171. if (defined(surfaceTile.vertexArray)) {
  1172. mesh = surfaceTile.mesh;
  1173. vertexArray = surfaceTile.vertexArray;
  1174. } else if (defined(surfaceTile.fill) && defined(surfaceTile.fill.vertexArray)) {
  1175. mesh = surfaceTile.fill.mesh;
  1176. vertexArray = surfaceTile.fill.vertexArray;
  1177. }
  1178. if (!defined(mesh) || !defined(vertexArray)) {
  1179. return;
  1180. }
  1181. if (defined(surfaceTile.wireframeVertexArray)) {
  1182. if (surfaceTile.wireframeVertexArray.mesh === mesh) {
  1183. return;
  1184. }
  1185. surfaceTile.wireframeVertexArray.destroy();
  1186. surfaceTile.wireframeVertexArray = undefined;
  1187. }
  1188. surfaceTile.wireframeVertexArray = createWireframeVertexArray(context, vertexArray, mesh);
  1189. surfaceTile.wireframeVertexArray.mesh = mesh;
  1190. }
  1191. /**
  1192. * Creates a vertex array for wireframe rendering of a terrain tile.
  1193. *
  1194. * @private
  1195. *
  1196. * @param {Context} context The context in which to create the vertex array.
  1197. * @param {VertexArray} vertexArray The existing, non-wireframe vertex array. The new vertex array
  1198. * will share vertex buffers with this existing one.
  1199. * @param {TerrainMesh} terrainMesh The terrain mesh containing non-wireframe indices.
  1200. * @returns {VertexArray} The vertex array for wireframe rendering.
  1201. */
  1202. function createWireframeVertexArray(context, vertexArray, terrainMesh) {
  1203. var indices = terrainMesh.indices;
  1204. var geometry = {
  1205. indices : indices,
  1206. primitiveType : PrimitiveType.TRIANGLES
  1207. };
  1208. GeometryPipeline.toWireframe(geometry);
  1209. var wireframeIndices = geometry.indices;
  1210. var wireframeIndexBuffer = Buffer.createIndexBuffer({
  1211. context : context,
  1212. typedArray : wireframeIndices,
  1213. usage : BufferUsage.STATIC_DRAW,
  1214. indexDatatype : IndexDatatype.fromSizeInBytes(wireframeIndices.BYTES_PER_ELEMENT)
  1215. });
  1216. return new VertexArray({
  1217. context : context,
  1218. attributes : vertexArray._attributes,
  1219. indexBuffer : wireframeIndexBuffer
  1220. });
  1221. }
  1222. var getDebugOrientedBoundingBox;
  1223. var getDebugBoundingSphere;
  1224. var debugDestroyPrimitive;
  1225. (function() {
  1226. var instanceOBB = new GeometryInstance({
  1227. geometry : BoxOutlineGeometry.fromDimensions({dimensions : new Cartesian3(2.0, 2.0, 2.0)})
  1228. });
  1229. var instanceSphere = new GeometryInstance({
  1230. geometry : new SphereOutlineGeometry({radius : 1.0})
  1231. });
  1232. var modelMatrix = new Matrix4();
  1233. var previousVolume;
  1234. var primitive;
  1235. function createDebugPrimitive(instance) {
  1236. return new Primitive({
  1237. geometryInstances : instance,
  1238. appearance : new PerInstanceColorAppearance({
  1239. translucent : false,
  1240. flat : true
  1241. }),
  1242. asynchronous : false
  1243. });
  1244. }
  1245. getDebugOrientedBoundingBox = function(obb, color) {
  1246. if (obb === previousVolume) {
  1247. return primitive;
  1248. }
  1249. debugDestroyPrimitive();
  1250. previousVolume = obb;
  1251. modelMatrix = Matrix4.fromRotationTranslation(obb.halfAxes, obb.center, modelMatrix);
  1252. instanceOBB.modelMatrix = modelMatrix;
  1253. instanceOBB.attributes.color = ColorGeometryInstanceAttribute.fromColor(color);
  1254. primitive = createDebugPrimitive(instanceOBB);
  1255. return primitive;
  1256. };
  1257. getDebugBoundingSphere = function(sphere, color) {
  1258. if (sphere === previousVolume) {
  1259. return primitive;
  1260. }
  1261. debugDestroyPrimitive();
  1262. previousVolume = sphere;
  1263. modelMatrix = Matrix4.fromTranslation(sphere.center, modelMatrix);
  1264. modelMatrix = Matrix4.multiplyByUniformScale(modelMatrix, sphere.radius, modelMatrix);
  1265. instanceSphere.modelMatrix = modelMatrix;
  1266. instanceSphere.attributes.color = ColorGeometryInstanceAttribute.fromColor(color);
  1267. primitive = createDebugPrimitive(instanceSphere);
  1268. return primitive;
  1269. };
  1270. debugDestroyPrimitive = function() {
  1271. if (defined(primitive)) {
  1272. primitive.destroy();
  1273. primitive = undefined;
  1274. previousVolume = undefined;
  1275. }
  1276. };
  1277. })();
  1278. var otherPassesInitialColor = new Cartesian4(0.0, 0.0, 0.0, 0.0);
  1279. var surfaceShaderSetOptionsScratch = {
  1280. frameState : undefined,
  1281. surfaceTile : undefined,
  1282. numberOfDayTextures : undefined,
  1283. applyBrightness : undefined,
  1284. applyContrast : undefined,
  1285. applyHue : undefined,
  1286. applySaturation : undefined,
  1287. applyGamma : undefined,
  1288. applyAlpha : undefined,
  1289. applySplit : undefined,
  1290. showReflectiveOcean : undefined,
  1291. showOceanWaves : undefined,
  1292. enableLighting : undefined,
  1293. showGroundAtmosphere : undefined,
  1294. perFragmentGroundAtmosphere : undefined,
  1295. hasVertexNormals : undefined,
  1296. useWebMercatorProjection : undefined,
  1297. enableFog : undefined,
  1298. enableClippingPlanes : undefined,
  1299. clippingPlanes : undefined,
  1300. clippedByBoundaries : undefined,
  1301. hasImageryLayerCutout : undefined,
  1302. colorCorrect : undefined,
  1303. colorToAlpha : undefined
  1304. };
  1305. function addDrawCommandsForTile(tileProvider, tile, frameState) {
  1306. var surfaceTile = tile.data;
  1307. if (!defined(surfaceTile.vertexArray)) {
  1308. if (surfaceTile.fill === undefined) {
  1309. // No fill was created for this tile, probably because this tile is not connected to
  1310. // any renderable tiles. So create a simple tile in the middle of the tile's possible
  1311. // height range.
  1312. surfaceTile.fill = new TerrainFillMesh(tile);
  1313. }
  1314. surfaceTile.fill.update(tileProvider, frameState);
  1315. }
  1316. var creditDisplay = frameState.creditDisplay;
  1317. var terrainData = surfaceTile.terrainData;
  1318. if (defined(terrainData) && defined(terrainData.credits)) {
  1319. var tileCredits = terrainData.credits;
  1320. for (var tileCreditIndex = 0,
  1321. tileCreditLength = tileCredits.length; tileCreditIndex < tileCreditLength; ++tileCreditIndex) {
  1322. creditDisplay.addCredit(tileCredits[tileCreditIndex]);
  1323. }
  1324. }
  1325. var maxTextures = ContextLimits.maximumTextureImageUnits;
  1326. var waterMaskTexture = surfaceTile.waterMaskTexture;
  1327. var waterMaskTranslationAndScale = surfaceTile.waterMaskTranslationAndScale;
  1328. if (!defined(waterMaskTexture) && defined(surfaceTile.fill)) {
  1329. waterMaskTexture = surfaceTile.fill.waterMaskTexture;
  1330. waterMaskTranslationAndScale = surfaceTile.fill.waterMaskTranslationAndScale;
  1331. }
  1332. var showReflectiveOcean = tileProvider.hasWaterMask && defined(waterMaskTexture);
  1333. var oceanNormalMap = tileProvider.oceanNormalMap;
  1334. var showOceanWaves = showReflectiveOcean && defined(oceanNormalMap);
  1335. var hasVertexNormals = tileProvider.terrainProvider.ready && tileProvider.terrainProvider.hasVertexNormals;
  1336. var enableFog = frameState.fog.enabled;
  1337. var showGroundAtmosphere = tileProvider.showGroundAtmosphere;
  1338. var castShadows = ShadowMode.castShadows(tileProvider.shadows);
  1339. var receiveShadows = ShadowMode.receiveShadows(tileProvider.shadows);
  1340. var hueShift = tileProvider.hueShift;
  1341. var saturationShift = tileProvider.saturationShift;
  1342. var brightnessShift = tileProvider.brightnessShift;
  1343. var colorCorrect = !(CesiumMath.equalsEpsilon(hueShift, 0.0, CesiumMath.EPSILON7) &&
  1344. CesiumMath.equalsEpsilon(saturationShift, 0.0, CesiumMath.EPSILON7) &&
  1345. CesiumMath.equalsEpsilon(brightnessShift, 0.0, CesiumMath.EPSILON7));
  1346. var perFragmentGroundAtmosphere = false;
  1347. if (showGroundAtmosphere) {
  1348. var mode = frameState.mode;
  1349. var camera = frameState.camera;
  1350. var cameraDistance;
  1351. if (mode === SceneMode.SCENE2D || mode === SceneMode.COLUMBUS_VIEW) {
  1352. cameraDistance = camera.positionCartographic.height;
  1353. } else {
  1354. cameraDistance = Cartesian3.magnitude(camera.positionWC);
  1355. }
  1356. var fadeOutDistance = tileProvider.nightFadeOutDistance;
  1357. if (mode !== SceneMode.SCENE3D) {
  1358. fadeOutDistance -= frameState.mapProjection.ellipsoid.maximumRadius;
  1359. }
  1360. perFragmentGroundAtmosphere = cameraDistance > fadeOutDistance;
  1361. }
  1362. if (showReflectiveOcean) {
  1363. --maxTextures;
  1364. }
  1365. if (showOceanWaves) {
  1366. --maxTextures;
  1367. }
  1368. if (defined(frameState.shadowState) && frameState.shadowState.shadowsEnabled) {
  1369. --maxTextures;
  1370. }
  1371. if (defined(tileProvider.clippingPlanes) && tileProvider.clippingPlanes.enabled) {
  1372. --maxTextures;
  1373. }
  1374. var mesh = surfaceTile.renderedMesh;
  1375. var rtc = mesh.center;
  1376. var encoding = mesh.encoding;
  1377. // Not used in 3D.
  1378. var tileRectangle = tileRectangleScratch;
  1379. // Only used for Mercator projections.
  1380. var southLatitude = 0.0;
  1381. var northLatitude = 0.0;
  1382. var southMercatorY = 0.0;
  1383. var oneOverMercatorHeight = 0.0;
  1384. var useWebMercatorProjection = false;
  1385. if (frameState.mode !== SceneMode.SCENE3D) {
  1386. var projection = frameState.mapProjection;
  1387. var southwest = projection.project(Rectangle.southwest(tile.rectangle), southwestScratch);
  1388. var northeast = projection.project(Rectangle.northeast(tile.rectangle), northeastScratch);
  1389. tileRectangle.x = southwest.x;
  1390. tileRectangle.y = southwest.y;
  1391. tileRectangle.z = northeast.x;
  1392. tileRectangle.w = northeast.y;
  1393. // In 2D and Columbus View, use the center of the tile for RTC rendering.
  1394. if (frameState.mode !== SceneMode.MORPHING) {
  1395. rtc = rtcScratch;
  1396. rtc.x = 0.0;
  1397. rtc.y = (tileRectangle.z + tileRectangle.x) * 0.5;
  1398. rtc.z = (tileRectangle.w + tileRectangle.y) * 0.5;
  1399. tileRectangle.x -= rtc.y;
  1400. tileRectangle.y -= rtc.z;
  1401. tileRectangle.z -= rtc.y;
  1402. tileRectangle.w -= rtc.z;
  1403. }
  1404. if (frameState.mode === SceneMode.SCENE2D && encoding.quantization === TerrainQuantization.BITS12) {
  1405. // In 2D, the texture coordinates of the tile are interpolated over the rectangle to get the position in the vertex shader.
  1406. // When the texture coordinates are quantized, error is introduced. This can be seen through the 1px wide cracking
  1407. // between the quantized tiles in 2D. To compensate for the error, move the expand the rectangle in each direction by
  1408. // half the error amount.
  1409. var epsilon = (1.0 / (Math.pow(2.0, 12.0) - 1.0)) * 0.5;
  1410. var widthEpsilon = (tileRectangle.z - tileRectangle.x) * epsilon;
  1411. var heightEpsilon = (tileRectangle.w - tileRectangle.y) * epsilon;
  1412. tileRectangle.x -= widthEpsilon;
  1413. tileRectangle.y -= heightEpsilon;
  1414. tileRectangle.z += widthEpsilon;
  1415. tileRectangle.w += heightEpsilon;
  1416. }
  1417. if (projection instanceof WebMercatorProjection) {
  1418. southLatitude = tile.rectangle.south;
  1419. northLatitude = tile.rectangle.north;
  1420. southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(southLatitude);
  1421. oneOverMercatorHeight = 1.0 / (WebMercatorProjection.geodeticLatitudeToMercatorAngle(northLatitude) - southMercatorY);
  1422. useWebMercatorProjection = true;
  1423. }
  1424. }
  1425. var surfaceShaderSetOptions = surfaceShaderSetOptionsScratch;
  1426. surfaceShaderSetOptions.frameState = frameState;
  1427. surfaceShaderSetOptions.surfaceTile = surfaceTile;
  1428. surfaceShaderSetOptions.showReflectiveOcean = showReflectiveOcean;
  1429. surfaceShaderSetOptions.showOceanWaves = showOceanWaves;
  1430. surfaceShaderSetOptions.enableLighting = tileProvider.enableLighting;
  1431. surfaceShaderSetOptions.showGroundAtmosphere = showGroundAtmosphere;
  1432. surfaceShaderSetOptions.perFragmentGroundAtmosphere = perFragmentGroundAtmosphere;
  1433. surfaceShaderSetOptions.hasVertexNormals = hasVertexNormals;
  1434. surfaceShaderSetOptions.useWebMercatorProjection = useWebMercatorProjection;
  1435. surfaceShaderSetOptions.clippedByBoundaries = surfaceTile.clippedByBoundaries;
  1436. var tileImageryCollection = surfaceTile.imagery;
  1437. var imageryIndex = 0;
  1438. var imageryLen = tileImageryCollection.length;
  1439. var firstPassRenderState = tileProvider._renderState;
  1440. var otherPassesRenderState = tileProvider._blendRenderState;
  1441. var renderState = firstPassRenderState;
  1442. var initialColor = tileProvider._firstPassInitialColor;
  1443. var context = frameState.context;
  1444. if (!defined(tileProvider._debug.boundingSphereTile)) {
  1445. debugDestroyPrimitive();
  1446. }
  1447. do {
  1448. var numberOfDayTextures = 0;
  1449. var command;
  1450. var uniformMap;
  1451. if (tileProvider._drawCommands.length <= tileProvider._usedDrawCommands) {
  1452. command = new DrawCommand();
  1453. command.owner = tile;
  1454. command.cull = false;
  1455. command.boundingVolume = new BoundingSphere();
  1456. command.orientedBoundingBox = undefined;
  1457. uniformMap = createTileUniformMap(frameState, tileProvider);
  1458. tileProvider._drawCommands.push(command);
  1459. tileProvider._uniformMaps.push(uniformMap);
  1460. } else {
  1461. command = tileProvider._drawCommands[tileProvider._usedDrawCommands];
  1462. uniformMap = tileProvider._uniformMaps[tileProvider._usedDrawCommands];
  1463. }
  1464. command.owner = tile;
  1465. ++tileProvider._usedDrawCommands;
  1466. if (tile === tileProvider._debug.boundingSphereTile) {
  1467. var obb = surfaceTile.orientedBoundingBox;
  1468. // If a debug primitive already exists for this tile, it will not be
  1469. // re-created, to avoid allocation every frame. If it were possible
  1470. // to have more than one selected tile, this would have to change.
  1471. if (defined(obb)) {
  1472. getDebugOrientedBoundingBox(obb, Color.RED).update(frameState);
  1473. } else if (defined(mesh) && defined(mesh.boundingSphere3D)) {
  1474. getDebugBoundingSphere(mesh.boundingSphere3D, Color.RED).update(frameState);
  1475. }
  1476. }
  1477. var uniformMapProperties = uniformMap.properties;
  1478. Cartesian4.clone(initialColor, uniformMapProperties.initialColor);
  1479. uniformMapProperties.oceanNormalMap = oceanNormalMap;
  1480. uniformMapProperties.lightingFadeDistance.x = tileProvider.lightingFadeOutDistance;
  1481. uniformMapProperties.lightingFadeDistance.y = tileProvider.lightingFadeInDistance;
  1482. uniformMapProperties.nightFadeDistance.x = tileProvider.nightFadeOutDistance;
  1483. uniformMapProperties.nightFadeDistance.y = tileProvider.nightFadeInDistance;
  1484. uniformMapProperties.zoomedOutOceanSpecularIntensity = tileProvider.zoomedOutOceanSpecularIntensity;
  1485. var highlightFillTile = !defined(surfaceTile.vertexArray) && defined(tileProvider.fillHighlightColor) && tileProvider.fillHighlightColor.alpha > 0.0;
  1486. if (highlightFillTile) {
  1487. Color.clone(tileProvider.fillHighlightColor, uniformMapProperties.fillHighlightColor);
  1488. }
  1489. uniformMapProperties.center3D = mesh.center;
  1490. Cartesian3.clone(rtc, uniformMapProperties.rtc);
  1491. Cartesian4.clone(tileRectangle, uniformMapProperties.tileRectangle);
  1492. uniformMapProperties.southAndNorthLatitude.x = southLatitude;
  1493. uniformMapProperties.southAndNorthLatitude.y = northLatitude;
  1494. uniformMapProperties.southMercatorYAndOneOverHeight.x = southMercatorY;
  1495. uniformMapProperties.southMercatorYAndOneOverHeight.y = oneOverMercatorHeight;
  1496. // Convert tile limiter rectangle from cartographic to texture space using the tileRectangle.
  1497. var localizedCartographicLimitRectangle = localizedCartographicLimitRectangleScratch;
  1498. var cartographicLimitRectangle = clipRectangleAntimeridian(tile.rectangle, tileProvider.cartographicLimitRectangle);
  1499. Cartesian3.fromElements(hueShift, saturationShift, brightnessShift, uniformMapProperties.hsbShift);
  1500. var cartographicTileRectangle = tile.rectangle;
  1501. var inverseTileWidth = 1.0 / cartographicTileRectangle.width;
  1502. var inverseTileHeight = 1.0 / cartographicTileRectangle.height;
  1503. localizedCartographicLimitRectangle.x = (cartographicLimitRectangle.west - cartographicTileRectangle.west) * inverseTileWidth;
  1504. localizedCartographicLimitRectangle.y = (cartographicLimitRectangle.south - cartographicTileRectangle.south) * inverseTileHeight;
  1505. localizedCartographicLimitRectangle.z = (cartographicLimitRectangle.east - cartographicTileRectangle.west) * inverseTileWidth;
  1506. localizedCartographicLimitRectangle.w = (cartographicLimitRectangle.north - cartographicTileRectangle.south) * inverseTileHeight;
  1507. Cartesian4.clone(localizedCartographicLimitRectangle, uniformMapProperties.localizedCartographicLimitRectangle);
  1508. // For performance, use fog in the shader only when the tile is in fog.
  1509. var applyFog = enableFog && CesiumMath.fog(tile._distance, frameState.fog.density) > CesiumMath.EPSILON3;
  1510. colorCorrect = colorCorrect && (applyFog || showGroundAtmosphere);
  1511. var applyBrightness = false;
  1512. var applyContrast = false;
  1513. var applyHue = false;
  1514. var applySaturation = false;
  1515. var applyGamma = false;
  1516. var applyAlpha = false;
  1517. var applySplit = false;
  1518. var applyCutout = false;
  1519. var applyColorToAlpha = false;
  1520. while (numberOfDayTextures < maxTextures && imageryIndex < imageryLen) {
  1521. var tileImagery = tileImageryCollection[imageryIndex];
  1522. var imagery = tileImagery.readyImagery;
  1523. ++imageryIndex;
  1524. if (!defined(imagery) || imagery.imageryLayer.alpha === 0.0) {
  1525. continue;
  1526. }
  1527. var texture = tileImagery.useWebMercatorT ? imagery.textureWebMercator : imagery.texture;
  1528. //>>includeStart('debug', pragmas.debug);
  1529. if (!defined(texture)) {
  1530. // Our "ready" texture isn't actually ready. This should never happen.
  1531. //
  1532. // Side note: It IS possible for it to not be in the READY ImageryState, though.
  1533. // This can happen when a single imagery tile is shared by two terrain tiles (common)
  1534. // and one of them (A) needs a geographic version of the tile because it is near the poles,
  1535. // and the other (B) does not. B can and will transition the imagery tile to the READY state
  1536. // without reprojecting to geographic. Then, later, A will deem that same tile not-ready-yet
  1537. // because it only has the Web Mercator texture, and flip it back to the TRANSITIONING state.
  1538. // The imagery tile won't be in the READY state anymore, but it's still READY enough for B's
  1539. // purposes.
  1540. throw new DeveloperError('readyImagery is not actually ready!');
  1541. }
  1542. //>>includeEnd('debug');
  1543. var imageryLayer = imagery.imageryLayer;
  1544. if (!defined(tileImagery.textureTranslationAndScale)) {
  1545. tileImagery.textureTranslationAndScale = imageryLayer._calculateTextureTranslationAndScale(tile, tileImagery);
  1546. }
  1547. uniformMapProperties.dayTextures[numberOfDayTextures] = texture;
  1548. uniformMapProperties.dayTextureTranslationAndScale[numberOfDayTextures] = tileImagery.textureTranslationAndScale;
  1549. uniformMapProperties.dayTextureTexCoordsRectangle[numberOfDayTextures] = tileImagery.textureCoordinateRectangle;
  1550. uniformMapProperties.dayTextureUseWebMercatorT[numberOfDayTextures] = tileImagery.useWebMercatorT;
  1551. uniformMapProperties.dayTextureAlpha[numberOfDayTextures] = imageryLayer.alpha;
  1552. applyAlpha = applyAlpha || uniformMapProperties.dayTextureAlpha[numberOfDayTextures] !== 1.0;
  1553. uniformMapProperties.dayTextureBrightness[numberOfDayTextures] = imageryLayer.brightness;
  1554. applyBrightness = applyBrightness || uniformMapProperties.dayTextureBrightness[numberOfDayTextures] !== ImageryLayer.DEFAULT_BRIGHTNESS;
  1555. uniformMapProperties.dayTextureContrast[numberOfDayTextures] = imageryLayer.contrast;
  1556. applyContrast = applyContrast || uniformMapProperties.dayTextureContrast[numberOfDayTextures] !== ImageryLayer.DEFAULT_CONTRAST;
  1557. uniformMapProperties.dayTextureHue[numberOfDayTextures] = imageryLayer.hue;
  1558. applyHue = applyHue || uniformMapProperties.dayTextureHue[numberOfDayTextures] !== ImageryLayer.DEFAULT_HUE;
  1559. uniformMapProperties.dayTextureSaturation[numberOfDayTextures] = imageryLayer.saturation;
  1560. applySaturation = applySaturation || uniformMapProperties.dayTextureSaturation[numberOfDayTextures] !== ImageryLayer.DEFAULT_SATURATION;
  1561. uniformMapProperties.dayTextureOneOverGamma[numberOfDayTextures] = 1.0 / imageryLayer.gamma;
  1562. applyGamma = applyGamma || uniformMapProperties.dayTextureOneOverGamma[numberOfDayTextures] !== 1.0 / ImageryLayer.DEFAULT_GAMMA;
  1563. uniformMapProperties.dayTextureSplit[numberOfDayTextures] = imageryLayer.splitDirection;
  1564. applySplit = applySplit || uniformMapProperties.dayTextureSplit[numberOfDayTextures] !== 0.0;
  1565. // Update cutout rectangle
  1566. var dayTextureCutoutRectangle = uniformMapProperties.dayTextureCutoutRectangles[numberOfDayTextures];
  1567. if (!defined(dayTextureCutoutRectangle)) {
  1568. dayTextureCutoutRectangle = uniformMapProperties.dayTextureCutoutRectangles[numberOfDayTextures] = new Cartesian4();
  1569. }
  1570. Cartesian4.clone(Cartesian4.ZERO, dayTextureCutoutRectangle);
  1571. if (defined(imageryLayer.cutoutRectangle)) {
  1572. var cutoutRectangle = clipRectangleAntimeridian(cartographicTileRectangle, imageryLayer.cutoutRectangle);
  1573. var intersection = Rectangle.simpleIntersection(cutoutRectangle, cartographicTileRectangle, rectangleIntersectionScratch);
  1574. applyCutout = defined(intersection) || applyCutout;
  1575. dayTextureCutoutRectangle.x = (cutoutRectangle.west - cartographicTileRectangle.west) * inverseTileWidth;
  1576. dayTextureCutoutRectangle.y = (cutoutRectangle.south - cartographicTileRectangle.south) * inverseTileHeight;
  1577. dayTextureCutoutRectangle.z = (cutoutRectangle.east - cartographicTileRectangle.west) * inverseTileWidth;
  1578. dayTextureCutoutRectangle.w = (cutoutRectangle.north - cartographicTileRectangle.south) * inverseTileHeight;
  1579. }
  1580. // Update color to alpha
  1581. var colorToAlpha = uniformMapProperties.colorsToAlpha[numberOfDayTextures];
  1582. if (!defined(colorToAlpha)) {
  1583. colorToAlpha = uniformMapProperties.colorsToAlpha[numberOfDayTextures] = new Cartesian4();
  1584. }
  1585. var hasColorToAlpha = defined(imageryLayer.colorToAlpha) && imageryLayer.colorToAlphaThreshold > 0.0;
  1586. applyColorToAlpha = applyColorToAlpha || hasColorToAlpha;
  1587. if (hasColorToAlpha) {
  1588. var color = imageryLayer.colorToAlpha;
  1589. colorToAlpha.x = color.red;
  1590. colorToAlpha.y = color.green;
  1591. colorToAlpha.z = color.blue;
  1592. colorToAlpha.w = imageryLayer.colorToAlphaThreshold;
  1593. } else {
  1594. colorToAlpha.w = -1.0;
  1595. }
  1596. if (defined(imagery.credits)) {
  1597. var credits = imagery.credits;
  1598. for (var creditIndex = 0, creditLength = credits.length; creditIndex < creditLength; ++creditIndex) {
  1599. creditDisplay.addCredit(credits[creditIndex]);
  1600. }
  1601. }
  1602. ++numberOfDayTextures;
  1603. }
  1604. // trim texture array to the used length so we don't end up using old textures
  1605. // which might get destroyed eventually
  1606. uniformMapProperties.dayTextures.length = numberOfDayTextures;
  1607. uniformMapProperties.waterMask = waterMaskTexture;
  1608. Cartesian4.clone(waterMaskTranslationAndScale, uniformMapProperties.waterMaskTranslationAndScale);
  1609. uniformMapProperties.minMaxHeight.x = encoding.minimumHeight;
  1610. uniformMapProperties.minMaxHeight.y = encoding.maximumHeight;
  1611. Matrix4.clone(encoding.matrix, uniformMapProperties.scaleAndBias);
  1612. // update clipping planes
  1613. var clippingPlanes = tileProvider._clippingPlanes;
  1614. var clippingPlanesEnabled = defined(clippingPlanes) && clippingPlanes.enabled && tile.isClipped;
  1615. if (clippingPlanesEnabled) {
  1616. uniformMapProperties.clippingPlanesEdgeColor = Color.clone(clippingPlanes.edgeColor, uniformMapProperties.clippingPlanesEdgeColor);
  1617. uniformMapProperties.clippingPlanesEdgeWidth = clippingPlanes.edgeWidth;
  1618. }
  1619. if (defined(tileProvider.uniformMap)) {
  1620. uniformMap = combine(uniformMap, tileProvider.uniformMap);
  1621. }
  1622. surfaceShaderSetOptions.numberOfDayTextures = numberOfDayTextures;
  1623. surfaceShaderSetOptions.applyBrightness = applyBrightness;
  1624. surfaceShaderSetOptions.applyContrast = applyContrast;
  1625. surfaceShaderSetOptions.applyHue = applyHue;
  1626. surfaceShaderSetOptions.applySaturation = applySaturation;
  1627. surfaceShaderSetOptions.applyGamma = applyGamma;
  1628. surfaceShaderSetOptions.applyAlpha = applyAlpha;
  1629. surfaceShaderSetOptions.applySplit = applySplit;
  1630. surfaceShaderSetOptions.enableFog = applyFog;
  1631. surfaceShaderSetOptions.enableClippingPlanes = clippingPlanesEnabled;
  1632. surfaceShaderSetOptions.clippingPlanes = clippingPlanes;
  1633. surfaceShaderSetOptions.hasImageryLayerCutout = applyCutout;
  1634. surfaceShaderSetOptions.colorCorrect = colorCorrect;
  1635. surfaceShaderSetOptions.highlightFillTile = highlightFillTile;
  1636. surfaceShaderSetOptions.colorToAlpha = applyColorToAlpha;
  1637. command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(surfaceShaderSetOptions);
  1638. command.castShadows = castShadows;
  1639. command.receiveShadows = receiveShadows;
  1640. command.renderState = renderState;
  1641. command.primitiveType = PrimitiveType.TRIANGLES;
  1642. command.vertexArray = surfaceTile.vertexArray || surfaceTile.fill.vertexArray;
  1643. command.uniformMap = uniformMap;
  1644. command.pass = Pass.GLOBE;
  1645. if (tileProvider._debug.wireframe) {
  1646. createWireframeVertexArrayIfNecessary(context, tileProvider, tile);
  1647. if (defined(surfaceTile.wireframeVertexArray)) {
  1648. command.vertexArray = surfaceTile.wireframeVertexArray;
  1649. command.primitiveType = PrimitiveType.LINES;
  1650. }
  1651. }
  1652. var boundingVolume = command.boundingVolume;
  1653. var orientedBoundingBox = command.orientedBoundingBox;
  1654. if (frameState.mode !== SceneMode.SCENE3D) {
  1655. var tileBoundingRegion = surfaceTile.tileBoundingRegion;
  1656. BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, tileBoundingRegion.minimumHeight, tileBoundingRegion.maximumHeight, boundingVolume);
  1657. Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center);
  1658. if (frameState.mode === SceneMode.MORPHING) {
  1659. boundingVolume = BoundingSphere.union(mesh.boundingSphere3D, boundingVolume, boundingVolume);
  1660. }
  1661. } else {
  1662. command.boundingVolume = BoundingSphere.clone(mesh.boundingSphere3D, boundingVolume);
  1663. command.orientedBoundingBox = OrientedBoundingBox.clone(surfaceTile.orientedBoundingBox, orientedBoundingBox);
  1664. }
  1665. command.dirty = true;
  1666. frameState.commandList.push(command);
  1667. renderState = otherPassesRenderState;
  1668. initialColor = otherPassesInitialColor;
  1669. } while (imageryIndex < imageryLen);
  1670. }
  1671. export default GlobeSurfaceTileProvider;