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

GroundPrimitive.js 31KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Cartographic from "../Core/Cartographic.js";
  5. import Check from "../Core/Check.js";
  6. import Frozen from "../Core/Frozen.js";
  7. import defined from "../Core/defined.js";
  8. import destroyObject from "../Core/destroyObject.js";
  9. import DeveloperError from "../Core/DeveloperError.js";
  10. import GeometryInstance from "../Core/GeometryInstance.js";
  11. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  12. import Rectangle from "../Core/Rectangle.js";
  13. import VerticalExaggeration from "../Core/VerticalExaggeration.js";
  14. import ClassificationPrimitive from "./ClassificationPrimitive.js";
  15. import ClassificationType from "./ClassificationType.js";
  16. import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";
  17. import SceneMode from "./SceneMode.js";
  18. import ShadowVolumeAppearance from "./ShadowVolumeAppearance.js";
  19. const GroundPrimitiveUniformMap = {
  20. u_globeMinimumAltitude: function () {
  21. return 55000.0;
  22. },
  23. };
  24. /**
  25. * A ground primitive represents geometry draped over terrain or 3D Tiles in the {@link Scene}.
  26. * <p>
  27. * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including
  28. * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement,
  29. * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix
  30. * and match most of them and add a new geometry or appearance independently of each other.
  31. * </p>
  32. * <p>
  33. * Support for the WEBGL_depth_texture extension is required to use GeometryInstances with different PerInstanceColors
  34. * or materials besides PerInstanceColorAppearance.
  35. * </p>
  36. * <p>
  37. * Textured GroundPrimitives were designed for notional patterns and are not meant for precisely mapping
  38. * textures to terrain - for that use case, use {@link SingleTileImageryProvider}.
  39. * </p>
  40. * <p>
  41. * For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there
  42. * will be rendering artifacts for some viewing angles.
  43. * </p>
  44. * <p>
  45. * Valid geometries are {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}.
  46. * </p>
  47. *
  48. * @alias GroundPrimitive
  49. * @constructor
  50. *
  51. * @param {object} [options] Object with the following properties:
  52. * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render.
  53. * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to a flat PerInstanceColorAppearance when GeometryInstances have a color attribute.
  54. * @param {boolean} [options.show=true] Determines if this primitive will be shown.
  55. * @param {boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  56. * @param {boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
  57. * @param {boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory.
  58. * @param {boolean} [options.releaseGeometryInstances=true] When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  59. * @param {boolean} [options.allowPicking=true] When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  60. * @param {boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first.
  61. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified.
  62. * @param {boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  63. * @param {boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be <code>true</code> on
  64. * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be <code>false</code>.
  65. *
  66. * @example
  67. * // Example 1: Create primitive with a single instance
  68. * const rectangleInstance = new Cesium.GeometryInstance({
  69. * geometry : new Cesium.RectangleGeometry({
  70. * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0)
  71. * }),
  72. * id : 'rectangle',
  73. * attributes : {
  74. * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
  75. * }
  76. * });
  77. * scene.primitives.add(new Cesium.GroundPrimitive({
  78. * geometryInstances : rectangleInstance
  79. * }));
  80. *
  81. * // Example 2: Batch instances
  82. * const color = new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5); // Both instances must have the same color.
  83. * const rectangleInstance = new Cesium.GeometryInstance({
  84. * geometry : new Cesium.RectangleGeometry({
  85. * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0)
  86. * }),
  87. * id : 'rectangle',
  88. * attributes : {
  89. * color : color
  90. * }
  91. * });
  92. * const ellipseInstance = new Cesium.GeometryInstance({
  93. * geometry : new Cesium.EllipseGeometry({
  94. * center : Cesium.Cartesian3.fromDegrees(-105.0, 40.0),
  95. * semiMinorAxis : 300000.0,
  96. * semiMajorAxis : 400000.0
  97. * }),
  98. * id : 'ellipse',
  99. * attributes : {
  100. * color : color
  101. * }
  102. * });
  103. * scene.primitives.add(new Cesium.GroundPrimitive({
  104. * geometryInstances : [rectangleInstance, ellipseInstance]
  105. * }));
  106. *
  107. * @see Primitive
  108. * @see ClassificationPrimitive
  109. * @see GeometryInstance
  110. * @see Appearance
  111. */
  112. function GroundPrimitive(options) {
  113. options = options ?? Frozen.EMPTY_OBJECT;
  114. let appearance = options.appearance;
  115. const geometryInstances = options.geometryInstances;
  116. if (!defined(appearance) && defined(geometryInstances)) {
  117. const geometryInstancesArray = Array.isArray(geometryInstances)
  118. ? geometryInstances
  119. : [geometryInstances];
  120. const geometryInstanceCount = geometryInstancesArray.length;
  121. for (let i = 0; i < geometryInstanceCount; i++) {
  122. const attributes = geometryInstancesArray[i].attributes;
  123. if (defined(attributes) && defined(attributes.color)) {
  124. appearance = new PerInstanceColorAppearance({
  125. flat: true,
  126. });
  127. break;
  128. }
  129. }
  130. }
  131. /**
  132. * The {@link Appearance} used to shade this primitive. Each geometry
  133. * instance is shaded with the same appearance. Some appearances, like
  134. * {@link PerInstanceColorAppearance} allow giving each instance unique
  135. * properties.
  136. *
  137. * @type Appearance
  138. *
  139. * @default undefined
  140. */
  141. this.appearance = appearance;
  142. /**
  143. * The geometry instances rendered with this primitive. This may
  144. * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>
  145. * is <code>true</code> when the primitive is constructed.
  146. * <p>
  147. * Changing this property after the primitive is rendered has no effect.
  148. * </p>
  149. *
  150. * @readonly
  151. * @type {Array|GeometryInstance}
  152. *
  153. * @default undefined
  154. */
  155. this.geometryInstances = options.geometryInstances;
  156. /**
  157. * Determines if the primitive will be shown. This affects all geometry
  158. * instances in the primitive.
  159. *
  160. * @type {boolean}
  161. *
  162. * @default true
  163. */
  164. this.show = options.show ?? true;
  165. /**
  166. * Determines whether terrain, 3D Tiles or both will be classified.
  167. *
  168. * @type {ClassificationType}
  169. *
  170. * @default ClassificationType.BOTH
  171. */
  172. this.classificationType =
  173. options.classificationType ?? ClassificationType.BOTH;
  174. /**
  175. * This property is for debugging only; it is not for production use nor is it optimized.
  176. * <p>
  177. * Draws the bounding sphere for each draw command in the primitive.
  178. * </p>
  179. *
  180. * @type {boolean}
  181. *
  182. * @default false
  183. */
  184. this.debugShowBoundingVolume = options.debugShowBoundingVolume ?? false;
  185. /**
  186. * This property is for debugging only; it is not for production use nor is it optimized.
  187. * <p>
  188. * Draws the shadow volume for each geometry in the primitive.
  189. * </p>
  190. *
  191. * @type {boolean}
  192. *
  193. * @default false
  194. */
  195. this.debugShowShadowVolume = options.debugShowShadowVolume ?? false;
  196. this._boundingVolumes = [];
  197. this._boundingVolumes2D = [];
  198. this._ready = false;
  199. this._primitive = undefined;
  200. this._maxHeight = undefined;
  201. this._minHeight = undefined;
  202. this._maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  203. this._minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  204. this._boundingSpheresKeys = [];
  205. this._boundingSpheres = [];
  206. this._useFragmentCulling = false;
  207. // Used when inserting in an OrderedPrimitiveCollection
  208. this._zIndex = undefined;
  209. const that = this;
  210. this._classificationPrimitiveOptions = {
  211. geometryInstances: undefined,
  212. appearance: undefined,
  213. vertexCacheOptimize: options.vertexCacheOptimize ?? false,
  214. interleave: options.interleave ?? false,
  215. releaseGeometryInstances: options.releaseGeometryInstances ?? true,
  216. allowPicking: options.allowPicking ?? true,
  217. asynchronous: options.asynchronous ?? true,
  218. compressVertices: options.compressVertices ?? true,
  219. _createBoundingVolumeFunction: undefined,
  220. _updateAndQueueCommandsFunction: undefined,
  221. _pickPrimitive: that,
  222. _extruded: true,
  223. _uniformMap: GroundPrimitiveUniformMap,
  224. };
  225. }
  226. Object.defineProperties(GroundPrimitive.prototype, {
  227. /**
  228. * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  229. *
  230. * @memberof GroundPrimitive.prototype
  231. *
  232. * @type {boolean}
  233. * @readonly
  234. *
  235. * @default true
  236. */
  237. vertexCacheOptimize: {
  238. get: function () {
  239. return this._classificationPrimitiveOptions.vertexCacheOptimize;
  240. },
  241. },
  242. /**
  243. * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
  244. *
  245. * @memberof GroundPrimitive.prototype
  246. *
  247. * @type {boolean}
  248. * @readonly
  249. *
  250. * @default false
  251. */
  252. interleave: {
  253. get: function () {
  254. return this._classificationPrimitiveOptions.interleave;
  255. },
  256. },
  257. /**
  258. * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  259. *
  260. * @memberof GroundPrimitive.prototype
  261. *
  262. * @type {boolean}
  263. * @readonly
  264. *
  265. * @default true
  266. */
  267. releaseGeometryInstances: {
  268. get: function () {
  269. return this._classificationPrimitiveOptions.releaseGeometryInstances;
  270. },
  271. },
  272. /**
  273. * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  274. *
  275. * @memberof GroundPrimitive.prototype
  276. *
  277. * @type {boolean}
  278. * @readonly
  279. *
  280. * @default true
  281. */
  282. allowPicking: {
  283. get: function () {
  284. return this._classificationPrimitiveOptions.allowPicking;
  285. },
  286. },
  287. /**
  288. * Determines if the geometry instances will be created and batched on a web worker.
  289. *
  290. * @memberof GroundPrimitive.prototype
  291. *
  292. * @type {boolean}
  293. * @readonly
  294. *
  295. * @default true
  296. */
  297. asynchronous: {
  298. get: function () {
  299. return this._classificationPrimitiveOptions.asynchronous;
  300. },
  301. },
  302. /**
  303. * When <code>true</code>, geometry vertices are compressed, which will save memory.
  304. *
  305. * @memberof GroundPrimitive.prototype
  306. *
  307. * @type {boolean}
  308. * @readonly
  309. *
  310. * @default true
  311. */
  312. compressVertices: {
  313. get: function () {
  314. return this._classificationPrimitiveOptions.compressVertices;
  315. },
  316. },
  317. /**
  318. * Determines if the primitive is complete and ready to render. If this property is
  319. * true, the primitive will be rendered the next time that {@link GroundPrimitive#update}
  320. * is called.
  321. *
  322. * @memberof GroundPrimitive.prototype
  323. *
  324. * @type {boolean}
  325. * @readonly
  326. */
  327. ready: {
  328. get: function () {
  329. return this._ready;
  330. },
  331. },
  332. });
  333. /**
  334. * Determines if GroundPrimitive rendering is supported.
  335. *
  336. * @function
  337. * @param {Scene} scene The scene.
  338. * @returns {boolean} <code>true</code> if GroundPrimitives are supported; otherwise, returns <code>false</code>
  339. */
  340. GroundPrimitive.isSupported = ClassificationPrimitive.isSupported;
  341. function getComputeMaximumHeightFunction(primitive) {
  342. return function (granularity, ellipsoid) {
  343. const r = ellipsoid.maximumRadius;
  344. const delta = r / Math.cos(granularity * 0.5) - r;
  345. return primitive._maxHeight + delta;
  346. };
  347. }
  348. function getComputeMinimumHeightFunction(primitive) {
  349. return function (granularity, ellipsoid) {
  350. return primitive._minHeight;
  351. };
  352. }
  353. const scratchBVCartesianHigh = new Cartesian3();
  354. const scratchBVCartesianLow = new Cartesian3();
  355. const scratchBVCartesian = new Cartesian3();
  356. const scratchBVCartographic = new Cartographic();
  357. const scratchBVRectangle = new Rectangle();
  358. function getRectangle(frameState, geometry) {
  359. const ellipsoid = frameState.mapProjection.ellipsoid;
  360. if (
  361. !defined(geometry.attributes) ||
  362. !defined(geometry.attributes.position3DHigh)
  363. ) {
  364. if (defined(geometry.rectangle)) {
  365. return geometry.rectangle;
  366. }
  367. return undefined;
  368. }
  369. const highPositions = geometry.attributes.position3DHigh.values;
  370. const lowPositions = geometry.attributes.position3DLow.values;
  371. const length = highPositions.length;
  372. let minLat = Number.POSITIVE_INFINITY;
  373. let minLon = Number.POSITIVE_INFINITY;
  374. let maxLat = Number.NEGATIVE_INFINITY;
  375. let maxLon = Number.NEGATIVE_INFINITY;
  376. for (let i = 0; i < length; i += 3) {
  377. const highPosition = Cartesian3.unpack(
  378. highPositions,
  379. i,
  380. scratchBVCartesianHigh,
  381. );
  382. const lowPosition = Cartesian3.unpack(
  383. lowPositions,
  384. i,
  385. scratchBVCartesianLow,
  386. );
  387. const position = Cartesian3.add(
  388. highPosition,
  389. lowPosition,
  390. scratchBVCartesian,
  391. );
  392. const cartographic = ellipsoid.cartesianToCartographic(
  393. position,
  394. scratchBVCartographic,
  395. );
  396. const latitude = cartographic.latitude;
  397. const longitude = cartographic.longitude;
  398. minLat = Math.min(minLat, latitude);
  399. minLon = Math.min(minLon, longitude);
  400. maxLat = Math.max(maxLat, latitude);
  401. maxLon = Math.max(maxLon, longitude);
  402. }
  403. const rectangle = scratchBVRectangle;
  404. rectangle.north = maxLat;
  405. rectangle.south = minLat;
  406. rectangle.east = maxLon;
  407. rectangle.west = minLon;
  408. return rectangle;
  409. }
  410. function setMinMaxTerrainHeights(primitive, rectangle, ellipsoid) {
  411. const result = ApproximateTerrainHeights.getMinimumMaximumHeights(
  412. rectangle,
  413. ellipsoid,
  414. );
  415. primitive._minTerrainHeight = result.minimumTerrainHeight;
  416. primitive._maxTerrainHeight = result.maximumTerrainHeight;
  417. }
  418. function createBoundingVolume(groundPrimitive, frameState, geometry) {
  419. const ellipsoid = frameState.mapProjection.ellipsoid;
  420. const rectangle = getRectangle(frameState, geometry);
  421. const obb = OrientedBoundingBox.fromRectangle(
  422. rectangle,
  423. groundPrimitive._minHeight,
  424. groundPrimitive._maxHeight,
  425. ellipsoid,
  426. );
  427. groundPrimitive._boundingVolumes.push(obb);
  428. if (!frameState.scene3DOnly) {
  429. const projection = frameState.mapProjection;
  430. const boundingVolume = BoundingSphere.fromRectangleWithHeights2D(
  431. rectangle,
  432. projection,
  433. groundPrimitive._maxHeight,
  434. groundPrimitive._minHeight,
  435. );
  436. Cartesian3.fromElements(
  437. boundingVolume.center.z,
  438. boundingVolume.center.x,
  439. boundingVolume.center.y,
  440. boundingVolume.center,
  441. );
  442. groundPrimitive._boundingVolumes2D.push(boundingVolume);
  443. }
  444. }
  445. function boundingVolumeIndex(commandIndex, length) {
  446. return Math.floor((commandIndex % length) / 2);
  447. }
  448. function updateAndQueueRenderCommand(
  449. groundPrimitive,
  450. command,
  451. frameState,
  452. modelMatrix,
  453. cull,
  454. boundingVolume,
  455. debugShowBoundingVolume,
  456. ) {
  457. // Use derived appearance command for 2D if needed
  458. const classificationPrimitive = groundPrimitive._primitive;
  459. if (
  460. frameState.mode !== SceneMode.SCENE3D &&
  461. command.shaderProgram === classificationPrimitive._spColor &&
  462. classificationPrimitive._needs2DShader
  463. ) {
  464. command = command.derivedCommands.appearance2D;
  465. }
  466. command.owner = groundPrimitive;
  467. command.modelMatrix = modelMatrix;
  468. command.boundingVolume = boundingVolume;
  469. command.cull = cull;
  470. command.debugShowBoundingVolume = debugShowBoundingVolume;
  471. frameState.commandList.push(command);
  472. }
  473. function updateAndQueuePickCommand(
  474. groundPrimitive,
  475. command,
  476. frameState,
  477. modelMatrix,
  478. cull,
  479. boundingVolume,
  480. ) {
  481. // Use derived pick command for 2D if needed
  482. const classificationPrimitive = groundPrimitive._primitive;
  483. if (
  484. frameState.mode !== SceneMode.SCENE3D &&
  485. command.shaderProgram === classificationPrimitive._spPick &&
  486. classificationPrimitive._needs2DShader
  487. ) {
  488. command = command.derivedCommands.pick2D;
  489. }
  490. command.owner = groundPrimitive;
  491. command.modelMatrix = modelMatrix;
  492. command.boundingVolume = boundingVolume;
  493. command.cull = cull;
  494. frameState.commandList.push(command);
  495. }
  496. function updateAndQueueCommands(
  497. groundPrimitive,
  498. frameState,
  499. colorCommands,
  500. pickCommands,
  501. modelMatrix,
  502. cull,
  503. debugShowBoundingVolume,
  504. twoPasses,
  505. ) {
  506. let boundingVolumes;
  507. if (frameState.mode === SceneMode.SCENE3D) {
  508. boundingVolumes = groundPrimitive._boundingVolumes;
  509. } else {
  510. boundingVolumes = groundPrimitive._boundingVolumes2D;
  511. }
  512. const classificationType = groundPrimitive.classificationType;
  513. const queueTerrainCommands =
  514. classificationType !== ClassificationType.CESIUM_3D_TILE;
  515. const queue3DTilesCommands =
  516. classificationType !== ClassificationType.TERRAIN;
  517. const passes = frameState.passes;
  518. const classificationPrimitive = groundPrimitive._primitive;
  519. let i;
  520. let boundingVolume;
  521. let command;
  522. if (passes.render) {
  523. const colorLength = colorCommands.length;
  524. for (i = 0; i < colorLength; ++i) {
  525. boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)];
  526. if (queueTerrainCommands) {
  527. command = colorCommands[i];
  528. updateAndQueueRenderCommand(
  529. groundPrimitive,
  530. command,
  531. frameState,
  532. modelMatrix,
  533. cull,
  534. boundingVolume,
  535. debugShowBoundingVolume,
  536. );
  537. }
  538. if (queue3DTilesCommands) {
  539. command = colorCommands[i].derivedCommands.tileset;
  540. updateAndQueueRenderCommand(
  541. groundPrimitive,
  542. command,
  543. frameState,
  544. modelMatrix,
  545. cull,
  546. boundingVolume,
  547. debugShowBoundingVolume,
  548. );
  549. }
  550. }
  551. if (frameState.invertClassification) {
  552. const ignoreShowCommands = classificationPrimitive._commandsIgnoreShow;
  553. const ignoreShowCommandsLength = ignoreShowCommands.length;
  554. for (i = 0; i < ignoreShowCommandsLength; ++i) {
  555. boundingVolume = boundingVolumes[i];
  556. command = ignoreShowCommands[i];
  557. updateAndQueueRenderCommand(
  558. groundPrimitive,
  559. command,
  560. frameState,
  561. modelMatrix,
  562. cull,
  563. boundingVolume,
  564. debugShowBoundingVolume,
  565. );
  566. }
  567. }
  568. }
  569. if (passes.pick) {
  570. const pickLength = pickCommands.length;
  571. let pickOffsets;
  572. if (!groundPrimitive._useFragmentCulling) {
  573. // Must be using pick offsets
  574. pickOffsets = classificationPrimitive._primitive._pickOffsets;
  575. }
  576. for (i = 0; i < pickLength; ++i) {
  577. boundingVolume = boundingVolumes[boundingVolumeIndex(i, pickLength)];
  578. if (!groundPrimitive._useFragmentCulling) {
  579. const pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)];
  580. boundingVolume = boundingVolumes[pickOffset.index];
  581. }
  582. if (queueTerrainCommands) {
  583. command = pickCommands[i];
  584. updateAndQueuePickCommand(
  585. groundPrimitive,
  586. command,
  587. frameState,
  588. modelMatrix,
  589. cull,
  590. boundingVolume,
  591. );
  592. }
  593. if (queue3DTilesCommands) {
  594. command = pickCommands[i].derivedCommands.tileset;
  595. updateAndQueuePickCommand(
  596. groundPrimitive,
  597. command,
  598. frameState,
  599. modelMatrix,
  600. cull,
  601. boundingVolume,
  602. );
  603. }
  604. }
  605. }
  606. }
  607. /**
  608. * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the
  609. * GroundPrimitive synchronously.
  610. *
  611. * @returns {Promise<void>} A promise that will resolve once the terrain heights have been loaded.
  612. *
  613. */
  614. GroundPrimitive.initializeTerrainHeights = function () {
  615. return ApproximateTerrainHeights.initialize();
  616. };
  617. /**
  618. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  619. * get the draw commands needed to render this primitive.
  620. * <p>
  621. * Do not call this function directly. This is documented just to
  622. * list the exceptions that may be propagated when the scene is rendered:
  623. * </p>
  624. *
  625. * @exception {DeveloperError} For synchronous GroundPrimitive, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve.
  626. * @exception {DeveloperError} All instance geometries must have the same primitiveType.
  627. * @exception {DeveloperError} Appearance and material have a uniform with the same name.
  628. */
  629. GroundPrimitive.prototype.update = function (frameState) {
  630. if (!defined(this._primitive) && !defined(this.geometryInstances)) {
  631. return;
  632. }
  633. if (!ApproximateTerrainHeights.initialized) {
  634. //>>includeStart('debug', pragmas.debug);
  635. if (!this.asynchronous) {
  636. throw new DeveloperError(
  637. "For synchronous GroundPrimitives, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve.",
  638. );
  639. }
  640. //>>includeEnd('debug');
  641. GroundPrimitive.initializeTerrainHeights();
  642. return;
  643. }
  644. const that = this;
  645. const primitiveOptions = this._classificationPrimitiveOptions;
  646. if (!defined(this._primitive)) {
  647. const ellipsoid = frameState.mapProjection.ellipsoid;
  648. let instance;
  649. let geometry;
  650. let instanceType;
  651. const instances = Array.isArray(this.geometryInstances)
  652. ? this.geometryInstances
  653. : [this.geometryInstances];
  654. const length = instances.length;
  655. const groundInstances = new Array(length);
  656. let i;
  657. let rectangle;
  658. for (i = 0; i < length; ++i) {
  659. instance = instances[i];
  660. geometry = instance.geometry;
  661. const instanceRectangle = getRectangle(frameState, geometry);
  662. if (!defined(rectangle)) {
  663. rectangle = Rectangle.clone(instanceRectangle);
  664. } else if (defined(instanceRectangle)) {
  665. Rectangle.union(rectangle, instanceRectangle, rectangle);
  666. }
  667. const id = instance.id;
  668. if (defined(id) && defined(instanceRectangle)) {
  669. const boundingSphere = ApproximateTerrainHeights.getBoundingSphere(
  670. instanceRectangle,
  671. ellipsoid,
  672. );
  673. this._boundingSpheresKeys.push(id);
  674. this._boundingSpheres.push(boundingSphere);
  675. }
  676. instanceType = geometry.constructor;
  677. if (!defined(instanceType) || !defined(instanceType.createShadowVolume)) {
  678. //>>includeStart('debug', pragmas.debug);
  679. throw new DeveloperError(
  680. "Not all of the geometry instances have GroundPrimitive support.",
  681. );
  682. //>>includeEnd('debug');
  683. }
  684. }
  685. // Now compute the min/max heights for the primitive
  686. setMinMaxTerrainHeights(this, rectangle, ellipsoid);
  687. const exaggeration = frameState.verticalExaggeration;
  688. const exaggerationRelativeHeight =
  689. frameState.verticalExaggerationRelativeHeight;
  690. this._minHeight = VerticalExaggeration.getHeight(
  691. this._minTerrainHeight,
  692. exaggeration,
  693. exaggerationRelativeHeight,
  694. );
  695. this._maxHeight = VerticalExaggeration.getHeight(
  696. this._maxTerrainHeight,
  697. exaggeration,
  698. exaggerationRelativeHeight,
  699. );
  700. const useFragmentCulling = GroundPrimitive._supportsMaterials(
  701. frameState.context,
  702. );
  703. this._useFragmentCulling = useFragmentCulling;
  704. if (useFragmentCulling) {
  705. // Determine whether to add spherical or planar extent attributes for computing texture coordinates.
  706. // This depends on the size of the GeometryInstances.
  707. let attributes;
  708. let usePlanarExtents = true;
  709. for (i = 0; i < length; ++i) {
  710. instance = instances[i];
  711. geometry = instance.geometry;
  712. rectangle = getRectangle(frameState, geometry);
  713. if (ShadowVolumeAppearance.shouldUseSphericalCoordinates(rectangle)) {
  714. usePlanarExtents = false;
  715. break;
  716. }
  717. }
  718. for (i = 0; i < length; ++i) {
  719. instance = instances[i];
  720. geometry = instance.geometry;
  721. instanceType = geometry.constructor;
  722. const boundingRectangle = getRectangle(frameState, geometry);
  723. const textureCoordinateRotationPoints =
  724. geometry.textureCoordinateRotationPoints;
  725. if (usePlanarExtents) {
  726. attributes =
  727. ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(
  728. boundingRectangle,
  729. textureCoordinateRotationPoints,
  730. ellipsoid,
  731. frameState.mapProjection,
  732. this._maxHeight,
  733. );
  734. } else {
  735. attributes =
  736. ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(
  737. boundingRectangle,
  738. textureCoordinateRotationPoints,
  739. ellipsoid,
  740. frameState.mapProjection,
  741. );
  742. }
  743. const instanceAttributes = instance.attributes;
  744. for (const attributeKey in instanceAttributes) {
  745. if (instanceAttributes.hasOwnProperty(attributeKey)) {
  746. attributes[attributeKey] = instanceAttributes[attributeKey];
  747. }
  748. }
  749. groundInstances[i] = new GeometryInstance({
  750. geometry: instanceType.createShadowVolume(
  751. geometry,
  752. getComputeMinimumHeightFunction(this),
  753. getComputeMaximumHeightFunction(this),
  754. ),
  755. attributes: attributes,
  756. id: instance.id,
  757. });
  758. }
  759. } else {
  760. // ClassificationPrimitive will check if the colors are all the same if it detects lack of fragment culling attributes
  761. for (i = 0; i < length; ++i) {
  762. instance = instances[i];
  763. geometry = instance.geometry;
  764. instanceType = geometry.constructor;
  765. groundInstances[i] = new GeometryInstance({
  766. geometry: instanceType.createShadowVolume(
  767. geometry,
  768. getComputeMinimumHeightFunction(this),
  769. getComputeMaximumHeightFunction(this),
  770. ),
  771. attributes: instance.attributes,
  772. id: instance.id,
  773. });
  774. }
  775. }
  776. primitiveOptions.geometryInstances = groundInstances;
  777. primitiveOptions.appearance = this.appearance;
  778. primitiveOptions._createBoundingVolumeFunction = function (
  779. frameState,
  780. geometry,
  781. ) {
  782. createBoundingVolume(that, frameState, geometry);
  783. };
  784. primitiveOptions._updateAndQueueCommandsFunction = function (
  785. primitive,
  786. frameState,
  787. colorCommands,
  788. pickCommands,
  789. modelMatrix,
  790. cull,
  791. debugShowBoundingVolume,
  792. twoPasses,
  793. ) {
  794. updateAndQueueCommands(
  795. that,
  796. frameState,
  797. colorCommands,
  798. pickCommands,
  799. modelMatrix,
  800. cull,
  801. debugShowBoundingVolume,
  802. twoPasses,
  803. );
  804. };
  805. this._primitive = new ClassificationPrimitive(primitiveOptions);
  806. }
  807. this._primitive.appearance = this.appearance;
  808. this._primitive.show = this.show;
  809. this._primitive.debugShowShadowVolume = this.debugShowShadowVolume;
  810. this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
  811. this._primitive.update(frameState);
  812. frameState.afterRender.push(() => {
  813. if (!this._ready && defined(this._primitive) && this._primitive.ready) {
  814. this._ready = true;
  815. if (this.releaseGeometryInstances) {
  816. this.geometryInstances = undefined;
  817. }
  818. }
  819. });
  820. };
  821. /**
  822. * @private
  823. */
  824. GroundPrimitive.prototype.getBoundingSphere = function (id) {
  825. const index = this._boundingSpheresKeys.indexOf(id);
  826. if (index !== -1) {
  827. return this._boundingSpheres[index];
  828. }
  829. return undefined;
  830. };
  831. /**
  832. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  833. *
  834. * @param {*} id The id of the {@link GeometryInstance}.
  835. * @returns {object} The typed array in the attribute's format or undefined if the is no instance with id.
  836. *
  837. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  838. *
  839. * @example
  840. * const attributes = primitive.getGeometryInstanceAttributes('an id');
  841. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  842. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  843. */
  844. GroundPrimitive.prototype.getGeometryInstanceAttributes = function (id) {
  845. //>>includeStart('debug', pragmas.debug);
  846. if (!defined(this._primitive)) {
  847. throw new DeveloperError(
  848. "must call update before calling getGeometryInstanceAttributes",
  849. );
  850. }
  851. //>>includeEnd('debug');
  852. return this._primitive.getGeometryInstanceAttributes(id);
  853. };
  854. /**
  855. * Returns true if this object was destroyed; otherwise, false.
  856. * <p>
  857. * If this object was destroyed, it should not be used; calling any function other than
  858. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  859. * </p>
  860. *
  861. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  862. *
  863. * @see GroundPrimitive#destroy
  864. */
  865. GroundPrimitive.prototype.isDestroyed = function () {
  866. return false;
  867. };
  868. /**
  869. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  870. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  871. * <p>
  872. * Once an object is destroyed, it should not be used; calling any function other than
  873. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  874. * assign the return value (<code>undefined</code>) to the object as done in the example.
  875. * </p>
  876. *
  877. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  878. *
  879. * @example
  880. * e = e && e.destroy();
  881. *
  882. * @see GroundPrimitive#isDestroyed
  883. */
  884. GroundPrimitive.prototype.destroy = function () {
  885. this._primitive = this._primitive && this._primitive.destroy();
  886. return destroyObject(this);
  887. };
  888. /**
  889. * Exposed for testing.
  890. *
  891. * @param {Context} context Rendering context
  892. * @returns {boolean} Whether or not the current context supports materials on GroundPrimitives.
  893. * @private
  894. */
  895. GroundPrimitive._supportsMaterials = function (context) {
  896. return context.depthTexture;
  897. };
  898. /**
  899. * Checks if the given Scene supports materials on GroundPrimitives.
  900. * Materials on GroundPrimitives require support for the WEBGL_depth_texture extension.
  901. *
  902. * @param {Scene} scene The current scene.
  903. * @returns {boolean} Whether or not the current scene supports materials on GroundPrimitives.
  904. */
  905. GroundPrimitive.supportsMaterials = function (scene) {
  906. //>>includeStart('debug', pragmas.debug);
  907. Check.typeOf.object("scene", scene);
  908. //>>includeEnd('debug');
  909. return GroundPrimitive._supportsMaterials(scene.frameState.context);
  910. };
  911. export default GroundPrimitive;