Globe.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. /*global define*/
  2. define([
  3. '../Core/BoundingRectangle',
  4. '../Core/BoundingSphere',
  5. '../Core/buildModuleUrl',
  6. '../Core/Cartesian2',
  7. '../Core/Cartesian3',
  8. '../Core/Cartographic',
  9. '../Core/combine',
  10. '../Core/ComponentDatatype',
  11. '../Core/defaultValue',
  12. '../Core/defined',
  13. '../Core/defineProperties',
  14. '../Core/destroyObject',
  15. '../Core/DeveloperError',
  16. '../Core/Ellipsoid',
  17. '../Core/EllipsoidTerrainProvider',
  18. '../Core/FeatureDetection',
  19. '../Core/GeographicProjection',
  20. '../Core/Geometry',
  21. '../Core/GeometryAttribute',
  22. '../Core/Intersect',
  23. '../Core/IntersectionTests',
  24. '../Core/loadImage',
  25. '../Core/Math',
  26. '../Core/Matrix4',
  27. '../Core/Occluder',
  28. '../Core/PrimitiveType',
  29. '../Core/Ray',
  30. '../Core/Rectangle',
  31. '../Core/Transforms',
  32. '../Renderer/BufferUsage',
  33. '../Renderer/ClearCommand',
  34. '../Renderer/DrawCommand',
  35. '../Renderer/ShaderSource',
  36. '../Shaders/GlobeFS',
  37. '../Shaders/GlobeFSDepth',
  38. '../Shaders/GlobeFSPole',
  39. '../Shaders/GlobeVS',
  40. '../Shaders/GlobeVSDepth',
  41. '../Shaders/GlobeVSPole',
  42. '../ThirdParty/when',
  43. './DepthFunction',
  44. './GlobeSurfaceShaderSet',
  45. './GlobeSurfaceTileProvider',
  46. './ImageryLayerCollection',
  47. './Pass',
  48. './QuadtreePrimitive',
  49. './SceneMode',
  50. './terrainAttributeLocations'
  51. ], function(
  52. BoundingRectangle,
  53. BoundingSphere,
  54. buildModuleUrl,
  55. Cartesian2,
  56. Cartesian3,
  57. Cartographic,
  58. combine,
  59. ComponentDatatype,
  60. defaultValue,
  61. defined,
  62. defineProperties,
  63. destroyObject,
  64. DeveloperError,
  65. Ellipsoid,
  66. EllipsoidTerrainProvider,
  67. FeatureDetection,
  68. GeographicProjection,
  69. Geometry,
  70. GeometryAttribute,
  71. Intersect,
  72. IntersectionTests,
  73. loadImage,
  74. CesiumMath,
  75. Matrix4,
  76. Occluder,
  77. PrimitiveType,
  78. Ray,
  79. Rectangle,
  80. Transforms,
  81. BufferUsage,
  82. ClearCommand,
  83. DrawCommand,
  84. ShaderSource,
  85. GlobeFS,
  86. GlobeFSDepth,
  87. GlobeFSPole,
  88. GlobeVS,
  89. GlobeVSDepth,
  90. GlobeVSPole,
  91. when,
  92. DepthFunction,
  93. GlobeSurfaceShaderSet,
  94. GlobeSurfaceTileProvider,
  95. ImageryLayerCollection,
  96. Pass,
  97. QuadtreePrimitive,
  98. SceneMode,
  99. terrainAttributeLocations) {
  100. "use strict";
  101. /**
  102. * The globe rendered in the scene, including its terrain ({@link Globe#terrainProvider})
  103. * and imagery layers ({@link Globe#imageryLayers}). Access the globe using {@link Scene#globe}.
  104. *
  105. * @alias Globe
  106. * @constructor
  107. *
  108. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] Determines the size and shape of the
  109. * globe.
  110. */
  111. var Globe = function(ellipsoid) {
  112. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  113. var terrainProvider = new EllipsoidTerrainProvider({
  114. ellipsoid : ellipsoid
  115. });
  116. var imageryLayerCollection = new ImageryLayerCollection();
  117. this._ellipsoid = ellipsoid;
  118. this._imageryLayerCollection = imageryLayerCollection;
  119. this._surfaceShaderSet = new GlobeSurfaceShaderSet();
  120. this._surfaceShaderSet.baseVertexShaderSource = new ShaderSource({
  121. sources : [GlobeVS]
  122. });
  123. this._surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
  124. sources : [GlobeFS]
  125. });
  126. this._surface = new QuadtreePrimitive({
  127. tileProvider : new GlobeSurfaceTileProvider({
  128. terrainProvider : terrainProvider,
  129. imageryLayers : imageryLayerCollection,
  130. surfaceShaderSet : this._surfaceShaderSet
  131. })
  132. });
  133. this._occluder = new Occluder(new BoundingSphere(Cartesian3.ZERO, ellipsoid.minimumRadius), Cartesian3.ZERO);
  134. this._rsColor = undefined;
  135. this._rsColorWithoutDepthTest = undefined;
  136. this._clearDepthCommand = new ClearCommand({
  137. depth : 1.0,
  138. stencil : 0,
  139. owner : this
  140. });
  141. this._depthCommand = new DrawCommand({
  142. boundingVolume : new BoundingSphere(Cartesian3.ZERO, ellipsoid.maximumRadius),
  143. pass : Pass.OPAQUE,
  144. owner : this
  145. });
  146. this._northPoleCommand = new DrawCommand({
  147. pass : Pass.OPAQUE,
  148. owner : this
  149. });
  150. this._southPoleCommand = new DrawCommand({
  151. pass : Pass.OPAQUE,
  152. owner : this
  153. });
  154. this._drawNorthPole = false;
  155. this._drawSouthPole = false;
  156. this._mode = SceneMode.SCENE3D;
  157. /**
  158. * The terrain provider providing surface geometry for this globe.
  159. * @type {TerrainProvider}
  160. */
  161. this.terrainProvider = terrainProvider;
  162. /**
  163. * Determines the color of the north pole. If the day tile provider imagery does not
  164. * extend over the north pole, it will be filled with this color before applying lighting.
  165. *
  166. * @type {Cartesian3}
  167. * @default Cartesian3(2.0 / 255.0, 6.0 / 255.0, 18.0 / 255.0)
  168. */
  169. this.northPoleColor = new Cartesian3(2.0 / 255.0, 6.0 / 255.0, 18.0 / 255.0);
  170. /**
  171. * Determines the color of the south pole. If the day tile provider imagery does not
  172. * extend over the south pole, it will be filled with this color before applying lighting.
  173. *
  174. * @type {Cartesian3}
  175. * @default Cartesian3(1.0, 1.0, 1.0)
  176. */
  177. this.southPoleColor = new Cartesian3(1.0, 1.0, 1.0);
  178. /**
  179. * Determines if the globe will be shown.
  180. *
  181. * @type {Boolean}
  182. * @default true
  183. */
  184. this.show = true;
  185. /**
  186. * The normal map to use for rendering waves in the ocean. Setting this property will
  187. * only have an effect if the configured terrain provider includes a water mask.
  188. *
  189. * @type {String}
  190. * @default buildModuleUrl('Assets/Textures/waterNormalsSmall.jpg')
  191. */
  192. this.oceanNormalMapUrl = buildModuleUrl('Assets/Textures/waterNormalsSmall.jpg');
  193. this._oceanNormalMapUrl = undefined;
  194. /**
  195. * True if primitives such as billboards, polylines, labels, etc. should be depth-tested
  196. * against the terrain surface, or false if such primitives should always be drawn on top
  197. * of terrain unless they're on the opposite side of the globe. The disadvantage of depth
  198. * testing primitives against terrain is that slight numerical noise or terrain level-of-detail
  199. * switched can sometimes make a primitive that should be on the surface disappear underneath it.
  200. *
  201. * @type {Boolean}
  202. * @default false
  203. */
  204. this.depthTestAgainstTerrain = false;
  205. /**
  206. * The maximum screen-space error used to drive level-of-detail refinement. Higher
  207. * values will provide better performance but lower visual quality.
  208. *
  209. * @type {Number}
  210. * @default 2
  211. */
  212. this.maximumScreenSpaceError = 2;
  213. /**
  214. * The size of the terrain tile cache, expressed as a number of tiles. Any additional
  215. * tiles beyond this number will be freed, as long as they aren't needed for rendering
  216. * this frame. A larger number will consume more memory but will show detail faster
  217. * when, for example, zooming out and then back in.
  218. *
  219. * @type {Number}
  220. * @default 100
  221. */
  222. this.tileCacheSize = 100;
  223. /**
  224. * Enable lighting the globe with the sun as a light source.
  225. *
  226. * @type {Boolean}
  227. * @default false
  228. */
  229. this.enableLighting = false;
  230. /**
  231. * The distance where everything becomes lit. This only takes effect
  232. * when <code>enableLighting</code> is <code>true</code>.
  233. *
  234. * @type {Number}
  235. * @default 6500000.0
  236. */
  237. this.lightingFadeOutDistance = 6500000.0;
  238. /**
  239. * The distance where lighting resumes. This only takes effect
  240. * when <code>enableLighting</code> is <code>true</code>.
  241. *
  242. * @type {Number}
  243. * @default 9000000.0
  244. */
  245. this.lightingFadeInDistance = 9000000.0;
  246. /**
  247. * True if an animated wave effect should be shown in areas of the globe
  248. * covered by water; otherwise, false. This property is ignored if the
  249. * <code>terrainProvider</code> does not provide a water mask.
  250. *
  251. * @type {Boolean}
  252. * @default true
  253. */
  254. this.showWaterEffect = true;
  255. this._oceanNormalMap = undefined;
  256. this._zoomedOutOceanSpecularIntensity = 0.5;
  257. this._lightingFadeDistance = new Cartesian2(this.lightingFadeOutDistance, this.lightingFadeInDistance);
  258. var that = this;
  259. this._drawUniforms = {
  260. u_zoomedOutOceanSpecularIntensity : function() {
  261. return that._zoomedOutOceanSpecularIntensity;
  262. },
  263. u_oceanNormalMap : function() {
  264. return that._oceanNormalMap;
  265. },
  266. u_lightingFadeDistance : function() {
  267. return that._lightingFadeDistance;
  268. }
  269. };
  270. };
  271. defineProperties(Globe.prototype, {
  272. /**
  273. * Gets an ellipsoid describing the shape of this globe.
  274. * @memberof Globe.prototype
  275. * @type {Ellipsoid}
  276. */
  277. ellipsoid : {
  278. get : function() {
  279. return this._ellipsoid;
  280. }
  281. },
  282. /**
  283. * Gets the collection of image layers that will be rendered on this globe.
  284. * @memberof Globe.prototype
  285. * @type {ImageryLayerCollection}
  286. */
  287. imageryLayers : {
  288. get : function() {
  289. return this._imageryLayerCollection;
  290. }
  291. },
  292. /**
  293. * Gets or sets the color of the globe when no imagery is available.
  294. * @memberof Globe.prototype
  295. * @type {Color}
  296. */
  297. baseColor : {
  298. get : function() {
  299. return this._surface.tileProvider.baseColor;
  300. },
  301. set : function(value) {
  302. this._surface.tileProvider.baseColor = value;
  303. }
  304. }
  305. });
  306. function createComparePickTileFunction(rayOrigin) {
  307. return function(a, b) {
  308. var aDist = BoundingSphere.distanceSquaredTo(a.pickBoundingSphere, rayOrigin);
  309. var bDist = BoundingSphere.distanceSquaredTo(b.pickBoundingSphere, rayOrigin);
  310. return aDist - bDist;
  311. };
  312. }
  313. var scratchArray = [];
  314. var scratchSphereIntersectionResult = {
  315. start : 0.0,
  316. stop : 0.0
  317. };
  318. /**
  319. * Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates.
  320. *
  321. * @param {Ray} ray The ray to test for intersection.
  322. * @param {Scene} scene The scene.
  323. * @param {Cartesian3} [result] The object onto which to store the result.
  324. * @returns {Cartesian3|undefined} The intersection or <code>undefined</code> if none was found.
  325. *
  326. * @example
  327. * // find intersection of ray through a pixel and the globe
  328. * var ray = viewer.camera.getPickRay(windowCoordinates);
  329. * var intersection = globe.pick(ray, scene);
  330. */
  331. Globe.prototype.pick = function(ray, scene, result) {
  332. //>>includeStart('debug', pragmas.debug);
  333. if (!defined(ray)) {
  334. throw new DeveloperError('ray is required');
  335. }
  336. if (!defined(scene)) {
  337. throw new DeveloperError('scene is required');
  338. }
  339. //>>includeEnd('debug');
  340. var mode = scene.mode;
  341. var projection = scene.mapProjection;
  342. var sphereIntersections = scratchArray;
  343. sphereIntersections.length = 0;
  344. var tilesToRender = this._surface._tilesToRender;
  345. var length = tilesToRender.length;
  346. var tile;
  347. var i;
  348. for (i = 0; i < length; ++i) {
  349. tile = tilesToRender[i];
  350. var tileData = tile.data;
  351. if (!defined(tileData)) {
  352. continue;
  353. }
  354. var boundingVolume = tileData.pickBoundingSphere;
  355. if (mode !== SceneMode.SCENE3D) {
  356. BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, projection, tileData.minimumHeight, tileData.maximumHeight, boundingVolume);
  357. Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center);
  358. } else {
  359. BoundingSphere.clone(tileData.boundingSphere3D, boundingVolume);
  360. }
  361. var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult);
  362. if (defined(boundingSphereIntersection)) {
  363. sphereIntersections.push(tileData);
  364. }
  365. }
  366. sphereIntersections.sort(createComparePickTileFunction(ray.origin));
  367. var intersection;
  368. length = sphereIntersections.length;
  369. for (i = 0; i < length; ++i) {
  370. intersection = sphereIntersections[i].pick(ray, scene, true, result);
  371. if (defined(intersection)) {
  372. break;
  373. }
  374. }
  375. return intersection;
  376. };
  377. var scratchGetHeightCartesian = new Cartesian3();
  378. var scratchGetHeightIntersection = new Cartesian3();
  379. var scratchGetHeightCartographic = new Cartographic();
  380. var scratchGetHeightRay = new Ray();
  381. /**
  382. * Get the height of the surface at a given cartographic.
  383. *
  384. * @param {Cartographic} cartographic The cartographic for which to find the height.
  385. * @returns {Number|undefined} The height of the cartographic or undefined if it could not be found.
  386. */
  387. Globe.prototype.getHeight = function(cartographic) {
  388. //>>includeStart('debug', pragmas.debug);
  389. if (!defined(cartographic)) {
  390. throw new DeveloperError('cartographic is required');
  391. }
  392. //>>includeEnd('debug');
  393. var levelZeroTiles = this._surface._levelZeroTiles;
  394. if (!defined(levelZeroTiles)) {
  395. return;
  396. }
  397. var tile;
  398. var i;
  399. var length = levelZeroTiles.length;
  400. for (i = 0; i < length; ++i) {
  401. tile = levelZeroTiles[i];
  402. if (Rectangle.contains(tile.rectangle, cartographic)) {
  403. break;
  404. }
  405. }
  406. if (!defined(tile) || !Rectangle.contains(tile.rectangle, cartographic)) {
  407. return undefined;
  408. }
  409. while (tile.renderable) {
  410. var children = tile.children;
  411. length = children.length;
  412. for (i = 0; i < length; ++i) {
  413. tile = children[i];
  414. if (Rectangle.contains(tile.rectangle, cartographic)) {
  415. break;
  416. }
  417. }
  418. }
  419. while (defined(tile) && (!defined(tile.data) || !defined(tile.data.pickTerrain))) {
  420. tile = tile.parent;
  421. }
  422. if (!defined(tile)) {
  423. return undefined;
  424. }
  425. var ellipsoid = this._surface._tileProvider.tilingScheme.ellipsoid;
  426. var cartesian = ellipsoid.cartographicToCartesian(cartographic, scratchGetHeightCartesian);
  427. var ray = scratchGetHeightRay;
  428. Cartesian3.normalize(cartesian, ray.direction);
  429. var intersection = tile.data.pick(ray, undefined, false, scratchGetHeightIntersection);
  430. if (!defined(intersection)) {
  431. return undefined;
  432. }
  433. return ellipsoid.cartesianToCartographic(intersection, scratchGetHeightCartographic).height;
  434. };
  435. var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : [];
  436. var scratchCartesian1 = new Cartesian3();
  437. var scratchCartesian2 = new Cartesian3();
  438. var scratchCartesian3 = new Cartesian3();
  439. var scratchCartesian4 = new Cartesian3();
  440. function computeDepthQuad(globe, frameState) {
  441. var radii = globe._ellipsoid.radii;
  442. var p = frameState.camera.positionWC;
  443. // Find the corresponding position in the scaled space of the ellipsoid.
  444. var q = Cartesian3.multiplyComponents(globe._ellipsoid.oneOverRadii, p, scratchCartesian1);
  445. var qMagnitude = Cartesian3.magnitude(q);
  446. var qUnit = Cartesian3.normalize(q, scratchCartesian2);
  447. // Determine the east and north directions at q.
  448. var eUnit = Cartesian3.normalize(Cartesian3.cross(Cartesian3.UNIT_Z, q, scratchCartesian3), scratchCartesian3);
  449. var nUnit = Cartesian3.normalize(Cartesian3.cross(qUnit, eUnit, scratchCartesian4), scratchCartesian4);
  450. // Determine the radius of the 'limb' of the ellipsoid.
  451. var wMagnitude = Math.sqrt(Cartesian3.magnitudeSquared(q) - 1.0);
  452. // Compute the center and offsets.
  453. var center = Cartesian3.multiplyByScalar(qUnit, 1.0 / qMagnitude, scratchCartesian1);
  454. var scalar = wMagnitude / qMagnitude;
  455. var eastOffset = Cartesian3.multiplyByScalar(eUnit, scalar, scratchCartesian2);
  456. var northOffset = Cartesian3.multiplyByScalar(nUnit, scalar, scratchCartesian3);
  457. // A conservative measure for the longitudes would be to use the min/max longitudes of the bounding frustum.
  458. var upperLeft = Cartesian3.add(center, northOffset, scratchCartesian4);
  459. Cartesian3.subtract(upperLeft, eastOffset, upperLeft);
  460. Cartesian3.multiplyComponents(radii, upperLeft, upperLeft);
  461. Cartesian3.pack(upperLeft, depthQuadScratch, 0);
  462. var lowerLeft = Cartesian3.subtract(center, northOffset, scratchCartesian4);
  463. Cartesian3.subtract(lowerLeft, eastOffset, lowerLeft);
  464. Cartesian3.multiplyComponents(radii, lowerLeft, lowerLeft);
  465. Cartesian3.pack(lowerLeft, depthQuadScratch, 3);
  466. var upperRight = Cartesian3.add(center, northOffset, scratchCartesian4);
  467. Cartesian3.add(upperRight, eastOffset, upperRight);
  468. Cartesian3.multiplyComponents(radii, upperRight, upperRight);
  469. Cartesian3.pack(upperRight, depthQuadScratch, 6);
  470. var lowerRight = Cartesian3.subtract(center, northOffset, scratchCartesian4);
  471. Cartesian3.add(lowerRight, eastOffset, lowerRight);
  472. Cartesian3.multiplyComponents(radii, lowerRight, lowerRight);
  473. Cartesian3.pack(lowerRight, depthQuadScratch, 9);
  474. return depthQuadScratch;
  475. }
  476. var rightScratch = new Cartesian3();
  477. var upScratch = new Cartesian3();
  478. var negativeZ = Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3());
  479. var cartographicScratch = new Cartographic(0.0, 0.0);
  480. var pt1Scratch = new Cartesian3();
  481. var pt2Scratch = new Cartesian3();
  482. function computePoleQuad(globe, frameState, maxLat, maxGivenLat, viewProjMatrix, viewportTransformation) {
  483. cartographicScratch.longitude = 0.0;
  484. cartographicScratch.latitude = maxGivenLat;
  485. var pt1 = globe._ellipsoid.cartographicToCartesian(cartographicScratch, pt1Scratch);
  486. cartographicScratch.longitude = Math.PI;
  487. var pt2 = globe._ellipsoid.cartographicToCartesian(cartographicScratch, pt2Scratch);
  488. var radius = Cartesian3.magnitude(Cartesian3.subtract(pt1, pt2, rightScratch), rightScratch) * 0.5;
  489. cartographicScratch.longitude = 0.0;
  490. cartographicScratch.latitude = maxLat;
  491. var center = globe._ellipsoid.cartographicToCartesian(cartographicScratch, pt1Scratch);
  492. var right;
  493. var dir = frameState.camera.direction;
  494. if (1.0 - Cartesian3.dot(negativeZ, dir) < CesiumMath.EPSILON6) {
  495. right = Cartesian3.UNIT_X;
  496. } else {
  497. right = Cartesian3.normalize(Cartesian3.cross(dir, Cartesian3.UNIT_Z, rightScratch), rightScratch);
  498. }
  499. var screenRight = Cartesian3.add(center, Cartesian3.multiplyByScalar(right, radius, rightScratch), rightScratch);
  500. var screenUp = Cartesian3.add(center, Cartesian3.multiplyByScalar(Cartesian3.normalize(Cartesian3.cross(Cartesian3.UNIT_Z, right, upScratch), upScratch), radius, upScratch), upScratch);
  501. Transforms.pointToGLWindowCoordinates(viewProjMatrix, viewportTransformation, center, center);
  502. Transforms.pointToGLWindowCoordinates(viewProjMatrix, viewportTransformation, screenRight, screenRight);
  503. Transforms.pointToGLWindowCoordinates(viewProjMatrix, viewportTransformation, screenUp, screenUp);
  504. var halfWidth = Math.floor(Math.max(Cartesian3.distance(screenUp, center), Cartesian3.distance(screenRight, center)));
  505. var halfHeight = halfWidth;
  506. return new BoundingRectangle(
  507. Math.floor(center.x) - halfWidth,
  508. Math.floor(center.y) - halfHeight,
  509. halfWidth * 2.0,
  510. halfHeight * 2.0);
  511. }
  512. var viewportScratch = new BoundingRectangle();
  513. var vpTransformScratch = new Matrix4();
  514. var polePositionsScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  515. function fillPoles(globe, context, frameState) {
  516. var terrainProvider = globe.terrainProvider;
  517. if (frameState.mode !== SceneMode.SCENE3D) {
  518. return;
  519. }
  520. if (!terrainProvider.ready) {
  521. return;
  522. }
  523. var terrainMaxRectangle = terrainProvider.tilingScheme.rectangle;
  524. var viewProjMatrix = context.uniformState.viewProjection;
  525. var viewport = viewportScratch;
  526. viewport.width = context.drawingBufferWidth;
  527. viewport.height = context.drawingBufferHeight;
  528. var viewportTransformation = Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, vpTransformScratch);
  529. var latitudeExtension = 0.05;
  530. var rectangle;
  531. var boundingVolume;
  532. var frustumCull;
  533. var occludeePoint;
  534. var occluded;
  535. var geometry;
  536. var rect;
  537. var occluder = globe._occluder;
  538. // handle north pole
  539. if (terrainMaxRectangle.north < CesiumMath.PI_OVER_TWO) {
  540. rectangle = new Rectangle(-Math.PI, terrainMaxRectangle.north, Math.PI, CesiumMath.PI_OVER_TWO);
  541. boundingVolume = BoundingSphere.fromRectangle3D(rectangle, globe._ellipsoid);
  542. frustumCull = frameState.cullingVolume.computeVisibility(boundingVolume) === Intersect.OUTSIDE;
  543. occludeePoint = Occluder.computeOccludeePointFromRectangle(rectangle, globe._ellipsoid);
  544. occluded = (occludeePoint && !occluder.isPointVisible(occludeePoint, 0.0)) || !occluder.isBoundingSphereVisible(boundingVolume);
  545. globe._drawNorthPole = !frustumCull && !occluded;
  546. if (globe._drawNorthPole) {
  547. rect = computePoleQuad(globe, frameState, rectangle.north, rectangle.south - latitudeExtension, viewProjMatrix, viewportTransformation);
  548. polePositionsScratch[0] = rect.x;
  549. polePositionsScratch[1] = rect.y;
  550. polePositionsScratch[2] = rect.x + rect.width;
  551. polePositionsScratch[3] = rect.y;
  552. polePositionsScratch[4] = rect.x + rect.width;
  553. polePositionsScratch[5] = rect.y + rect.height;
  554. polePositionsScratch[6] = rect.x;
  555. polePositionsScratch[7] = rect.y + rect.height;
  556. if (!defined(globe._northPoleCommand.vertexArray)) {
  557. globe._northPoleCommand.boundingVolume = BoundingSphere.fromRectangle3D(rectangle, globe._ellipsoid);
  558. geometry = new Geometry({
  559. attributes : {
  560. position : new GeometryAttribute({
  561. componentDatatype : ComponentDatatype.FLOAT,
  562. componentsPerAttribute : 2,
  563. values : polePositionsScratch
  564. })
  565. }
  566. });
  567. globe._northPoleCommand.vertexArray = context.createVertexArrayFromGeometry({
  568. geometry : geometry,
  569. attributeLocations : {
  570. position : 0
  571. },
  572. bufferUsage : BufferUsage.STREAM_DRAW
  573. });
  574. } else {
  575. globe._northPoleCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(polePositionsScratch);
  576. }
  577. }
  578. }
  579. // handle south pole
  580. if (terrainMaxRectangle.south > -CesiumMath.PI_OVER_TWO) {
  581. rectangle = new Rectangle(-Math.PI, -CesiumMath.PI_OVER_TWO, Math.PI, terrainMaxRectangle.south);
  582. boundingVolume = BoundingSphere.fromRectangle3D(rectangle, globe._ellipsoid);
  583. frustumCull = frameState.cullingVolume.computeVisibility(boundingVolume) === Intersect.OUTSIDE;
  584. occludeePoint = Occluder.computeOccludeePointFromRectangle(rectangle, globe._ellipsoid);
  585. occluded = (occludeePoint && !occluder.isPointVisible(occludeePoint)) || !occluder.isBoundingSphereVisible(boundingVolume);
  586. globe._drawSouthPole = !frustumCull && !occluded;
  587. if (globe._drawSouthPole) {
  588. rect = computePoleQuad(globe, frameState, rectangle.south, rectangle.north + latitudeExtension, viewProjMatrix, viewportTransformation);
  589. polePositionsScratch[0] = rect.x;
  590. polePositionsScratch[1] = rect.y;
  591. polePositionsScratch[2] = rect.x + rect.width;
  592. polePositionsScratch[3] = rect.y;
  593. polePositionsScratch[4] = rect.x + rect.width;
  594. polePositionsScratch[5] = rect.y + rect.height;
  595. polePositionsScratch[6] = rect.x;
  596. polePositionsScratch[7] = rect.y + rect.height;
  597. if (!defined(globe._southPoleCommand.vertexArray)) {
  598. globe._southPoleCommand.boundingVolume = BoundingSphere.fromRectangle3D(rectangle, globe._ellipsoid);
  599. geometry = new Geometry({
  600. attributes : {
  601. position : new GeometryAttribute({
  602. componentDatatype : ComponentDatatype.FLOAT,
  603. componentsPerAttribute : 2,
  604. values : polePositionsScratch
  605. })
  606. }
  607. });
  608. globe._southPoleCommand.vertexArray = context.createVertexArrayFromGeometry({
  609. geometry : geometry,
  610. attributeLocations : {
  611. position : 0
  612. },
  613. bufferUsage : BufferUsage.STREAM_DRAW
  614. });
  615. } else {
  616. globe._southPoleCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(polePositionsScratch);
  617. }
  618. }
  619. }
  620. var poleIntensity = 0.0;
  621. var baseLayer = globe._imageryLayerCollection.length > 0 ? globe._imageryLayerCollection.get(0) : undefined;
  622. if (defined(baseLayer) && defined(baseLayer.imageryProvider) && defined(baseLayer.imageryProvider.getPoleIntensity)) {
  623. poleIntensity = baseLayer.imageryProvider.getPoleIntensity();
  624. }
  625. var drawUniforms = {
  626. u_dayIntensity : function() {
  627. return poleIntensity;
  628. }
  629. };
  630. if (!defined(globe._northPoleCommand.uniformMap)) {
  631. var northPoleUniforms = combine(drawUniforms, {
  632. u_color : function() {
  633. return globe.northPoleColor;
  634. }
  635. });
  636. globe._northPoleCommand.uniformMap = combine(northPoleUniforms, globe._drawUniforms);
  637. }
  638. if (!defined(globe._southPoleCommand.uniformMap)) {
  639. var southPoleUniforms = combine(drawUniforms, {
  640. u_color : function() {
  641. return globe.southPoleColor;
  642. }
  643. });
  644. globe._southPoleCommand.uniformMap = combine(southPoleUniforms, globe._drawUniforms);
  645. }
  646. }
  647. /**
  648. * @private
  649. */
  650. Globe.prototype.update = function(context, frameState, commandList) {
  651. if (!this.show) {
  652. return;
  653. }
  654. var width = context.drawingBufferWidth;
  655. var height = context.drawingBufferHeight;
  656. if (width === 0 || height === 0) {
  657. return;
  658. }
  659. var mode = frameState.mode;
  660. var projection = frameState.mapProjection;
  661. var modeChanged = false;
  662. if (this._mode !== mode || !defined(this._rsColor)) {
  663. modeChanged = true;
  664. if (mode === SceneMode.SCENE3D || mode === SceneMode.COLUMBUS_VIEW) {
  665. this._rsColor = context.createRenderState({ // Write color and depth
  666. cull : {
  667. enabled : true
  668. },
  669. depthTest : {
  670. enabled : true
  671. }
  672. });
  673. this._rsColorWithoutDepthTest = context.createRenderState({ // Write color, not depth
  674. cull : {
  675. enabled : true
  676. }
  677. });
  678. this._depthCommand.renderState = context.createRenderState({ // Write depth, not color
  679. cull : {
  680. enabled : true
  681. },
  682. depthTest : {
  683. enabled : true,
  684. func : DepthFunction.ALWAYS
  685. },
  686. colorMask : {
  687. red : false,
  688. green : false,
  689. blue : false,
  690. alpha : false
  691. }
  692. });
  693. } else {
  694. this._rsColor = context.createRenderState({
  695. cull : {
  696. enabled : true
  697. }
  698. });
  699. this._rsColorWithoutDepthTest = context.createRenderState({
  700. cull : {
  701. enabled : true
  702. }
  703. });
  704. this._depthCommand.renderState = context.createRenderState({
  705. cull : {
  706. enabled : true
  707. }
  708. });
  709. }
  710. }
  711. this._mode = mode;
  712. var northPoleCommand = this._northPoleCommand;
  713. var southPoleCommand = this._southPoleCommand;
  714. northPoleCommand.renderState = this._rsColorWithoutDepthTest;
  715. southPoleCommand.renderState = this._rsColorWithoutDepthTest;
  716. // update depth plane
  717. var depthQuad = computeDepthQuad(this, frameState);
  718. // depth plane
  719. if (!this._depthCommand.vertexArray) {
  720. var geometry = new Geometry({
  721. attributes : {
  722. position : new GeometryAttribute({
  723. componentDatatype : ComponentDatatype.FLOAT,
  724. componentsPerAttribute : 3,
  725. values : depthQuad
  726. })
  727. },
  728. indices : [0, 1, 2, 2, 1, 3],
  729. primitiveType : PrimitiveType.TRIANGLES
  730. });
  731. this._depthCommand.vertexArray = context.createVertexArrayFromGeometry({
  732. geometry : geometry,
  733. attributeLocations : {
  734. position : 0
  735. },
  736. bufferUsage : BufferUsage.DYNAMIC_DRAW
  737. });
  738. } else {
  739. this._depthCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(depthQuad);
  740. }
  741. if (!defined(this._depthCommand.shaderProgram)) {
  742. this._depthCommand.shaderProgram = context.createShaderProgram(GlobeVSDepth, GlobeFSDepth, {
  743. position : 0
  744. });
  745. }
  746. var surface = this._surface;
  747. var tileProvider = surface.tileProvider;
  748. var terrainProvider = this.terrainProvider;
  749. var hasWaterMask = this.showWaterEffect && terrainProvider.ready && terrainProvider.hasWaterMask;
  750. if (hasWaterMask && this.oceanNormalMapUrl !== this._oceanNormalMapUrl) {
  751. // url changed, load new normal map asynchronously
  752. var oceanNormalMapUrl = this.oceanNormalMapUrl;
  753. this._oceanNormalMapUrl = oceanNormalMapUrl;
  754. if (defined(oceanNormalMapUrl)) {
  755. var that = this;
  756. when(loadImage(oceanNormalMapUrl), function(image) {
  757. if (oceanNormalMapUrl !== that.oceanNormalMapUrl) {
  758. // url changed while we were loading
  759. return;
  760. }
  761. that._oceanNormalMap = that._oceanNormalMap && that._oceanNormalMap.destroy();
  762. that._oceanNormalMap = context.createTexture2D({
  763. source : image
  764. });
  765. });
  766. } else {
  767. this._oceanNormalMap = this._oceanNormalMap && this._oceanNormalMap.destroy();
  768. }
  769. }
  770. if (!defined(northPoleCommand.shaderProgram) ||
  771. !defined(southPoleCommand.shaderProgram)) {
  772. var poleShaderProgram = context.replaceShaderProgram(northPoleCommand.shaderProgram, GlobeVSPole, GlobeFSPole, terrainAttributeLocations);
  773. northPoleCommand.shaderProgram = poleShaderProgram;
  774. southPoleCommand.shaderProgram = poleShaderProgram;
  775. }
  776. this._occluder.cameraPosition = frameState.camera.positionWC;
  777. fillPoles(this, context, frameState);
  778. var pass = frameState.passes;
  779. if (pass.render) {
  780. // render quads to fill the poles
  781. if (mode === SceneMode.SCENE3D) {
  782. if (this._drawNorthPole) {
  783. commandList.push(northPoleCommand);
  784. }
  785. if (this._drawSouthPole) {
  786. commandList.push(southPoleCommand);
  787. }
  788. }
  789. // Don't show the ocean specular highlights when zoomed out in 2D and Columbus View.
  790. if (mode === SceneMode.SCENE3D) {
  791. this._zoomedOutOceanSpecularIntensity = 0.5;
  792. } else {
  793. this._zoomedOutOceanSpecularIntensity = 0.0;
  794. }
  795. surface.maximumScreenSpaceError = this.maximumScreenSpaceError;
  796. surface.tileCacheSize = this.tileCacheSize;
  797. tileProvider.terrainProvider = this.terrainProvider;
  798. tileProvider.lightingFadeOutDistance = this.lightingFadeOutDistance;
  799. tileProvider.lightingFadeInDistance = this.lightingFadeInDistance;
  800. tileProvider.zoomedOutOceanSpecularIntensity = this._zoomedOutOceanSpecularIntensity;
  801. tileProvider.hasWaterMask = hasWaterMask;
  802. tileProvider.oceanNormalMap = this._oceanNormalMap;
  803. tileProvider.enableLighting = this.enableLighting;
  804. surface.update(context, frameState, commandList);
  805. // render depth plane
  806. if (mode === SceneMode.SCENE3D || mode === SceneMode.COLUMBUS_VIEW) {
  807. if (!this.depthTestAgainstTerrain) {
  808. commandList.push(this._clearDepthCommand);
  809. if (mode === SceneMode.SCENE3D) {
  810. commandList.push(this._depthCommand);
  811. }
  812. }
  813. }
  814. }
  815. if (pass.pick) {
  816. // Not actually pickable, but render depth-only so primitives on the backface
  817. // of the globe are not picked.
  818. commandList.push(this._depthCommand);
  819. }
  820. };
  821. /**
  822. * Returns true if this object was destroyed; otherwise, false.
  823. * <br /><br />
  824. * If this object was destroyed, it should not be used; calling any function other than
  825. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  826. *
  827. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  828. *
  829. * @see Globe#destroy
  830. */
  831. Globe.prototype.isDestroyed = function() {
  832. return false;
  833. };
  834. /**
  835. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  836. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  837. * <br /><br />
  838. * Once an object is destroyed, it should not be used; calling any function other than
  839. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  840. * assign the return value (<code>undefined</code>) to the object as done in the example.
  841. *
  842. * @returns {undefined}
  843. *
  844. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  845. *
  846. * @see Globe#isDestroyed
  847. *
  848. * @example
  849. * globe = globe && globe.destroy();
  850. */
  851. Globe.prototype.destroy = function() {
  852. this._northPoleCommand.vertexArray = this._northPoleCommand.vertexArray && this._northPoleCommand.vertexArray.destroy();
  853. this._southPoleCommand.vertexArray = this._southPoleCommand.vertexArray && this._southPoleCommand.vertexArray.destroy();
  854. this._surfaceShaderSet = this._surfaceShaderSet && this._surfaceShaderSet.destroy();
  855. this._northPoleCommand.shaderProgram = this._northPoleCommand.shaderProgram && this._northPoleCommand.shaderProgram.destroy();
  856. this._southPoleCommand.shaderProgram = this._northPoleCommand.shaderProgram;
  857. this._depthCommand.shaderProgram = this._depthCommand.shaderProgram && this._depthCommand.shaderProgram.destroy();
  858. this._depthCommand.vertexArray = this._depthCommand.vertexArray && this._depthCommand.vertexArray.destroy();
  859. this._surface = this._surface && this._surface.destroy();
  860. this._oceanNormalMap = this._oceanNormalMap && this._oceanNormalMap.destroy();
  861. return destroyObject(this);
  862. };
  863. return Globe;
  864. });