GroundPolylinePrimitive.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import defaultValue from "../Core/defaultValue.js";
  4. import defer from "../Core/defer.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import DeveloperError from "../Core/DeveloperError.js";
  8. import GeometryInstance from "../Core/GeometryInstance.js";
  9. import GeometryInstanceAttribute from "../Core/GeometryInstanceAttribute.js";
  10. import GroundPolylineGeometry from "../Core/GroundPolylineGeometry.js";
  11. import DrawCommand from "../Renderer/DrawCommand.js";
  12. import Pass from "../Renderer/Pass.js";
  13. import RenderState from "../Renderer/RenderState.js";
  14. import ShaderProgram from "../Renderer/ShaderProgram.js";
  15. import ShaderSource from "../Renderer/ShaderSource.js";
  16. import PolylineShadowVolumeFS from "../Shaders/PolylineShadowVolumeFS.js";
  17. import PolylineShadowVolumeMorphFS from "../Shaders/PolylineShadowVolumeMorphFS.js";
  18. import PolylineShadowVolumeMorphVS from "../Shaders/PolylineShadowVolumeMorphVS.js";
  19. import PolylineShadowVolumeVS from "../Shaders/PolylineShadowVolumeVS.js";
  20. import BlendingState from "./BlendingState.js";
  21. import ClassificationType from "./ClassificationType.js";
  22. import CullFace from "./CullFace.js";
  23. import PolylineColorAppearance from "./PolylineColorAppearance.js";
  24. import PolylineMaterialAppearance from "./PolylineMaterialAppearance.js";
  25. import Primitive from "./Primitive.js";
  26. import SceneMode from "./SceneMode.js";
  27. import StencilConstants from "./StencilConstants.js";
  28. import StencilFunction from "./StencilFunction.js";
  29. import StencilOperation from "./StencilOperation.js";
  30. /**
  31. * A GroundPolylinePrimitive represents a polyline draped over the terrain or 3D Tiles in the {@link Scene}.
  32. * <p>
  33. * Only to be used with GeometryInstances containing {@link GroundPolylineGeometry}.
  34. * </p>
  35. *
  36. * @alias GroundPolylinePrimitive
  37. * @constructor
  38. *
  39. * @param {Object} [options] Object with the following properties:
  40. * @param {Array|GeometryInstance} [options.geometryInstances] GeometryInstances containing GroundPolylineGeometry
  41. * @param {Appearance} [options.appearance] The Appearance used to render the polyline. Defaults to a white color {@link Material} on a {@link PolylineMaterialAppearance}.
  42. * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
  43. * @param {Boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
  44. * @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.
  45. * @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.
  46. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first.
  47. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified.
  48. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  49. * @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 creation to have effect.
  50. *
  51. * @example
  52. * // 1. Draw a polyline on terrain with a basic color material
  53. *
  54. * const instance = new Cesium.GeometryInstance({
  55. * geometry : new Cesium.GroundPolylineGeometry({
  56. * positions : Cesium.Cartesian3.fromDegreesArray([
  57. * -112.1340164450331, 36.05494287836128,
  58. * -112.08821010582645, 36.097804071380715
  59. * ]),
  60. * width : 4.0
  61. * }),
  62. * id : 'object returned when this instance is picked and to get/set per-instance attributes'
  63. * });
  64. *
  65. * scene.groundPrimitives.add(new Cesium.GroundPolylinePrimitive({
  66. * geometryInstances : instance,
  67. * appearance : new Cesium.PolylineMaterialAppearance()
  68. * }));
  69. *
  70. * // 2. Draw a looped polyline on terrain with per-instance color and a distance display condition.
  71. * // Distance display conditions for polylines on terrain are based on an approximate terrain height
  72. * // instead of true terrain height.
  73. *
  74. * const instance2 = new Cesium.GeometryInstance({
  75. * geometry : new Cesium.GroundPolylineGeometry({
  76. * positions : Cesium.Cartesian3.fromDegreesArray([
  77. * -112.1340164450331, 36.05494287836128,
  78. * -112.08821010582645, 36.097804071380715,
  79. * -112.13296079730024, 36.168769146801104
  80. * ]),
  81. * loop : true,
  82. * width : 4.0
  83. * }),
  84. * attributes : {
  85. * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('green').withAlpha(0.7)),
  86. * distanceDisplayCondition : new Cesium.DistanceDisplayConditionGeometryInstanceAttribute(1000, 30000)
  87. * },
  88. * id : 'object returned when this instance is picked and to get/set per-instance attributes'
  89. * });
  90. *
  91. * scene.groundPrimitives.add(new Cesium.GroundPolylinePrimitive({
  92. * geometryInstances : instance2,
  93. * appearance : new Cesium.PolylineColorAppearance()
  94. * }));
  95. */
  96. function GroundPolylinePrimitive(options) {
  97. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  98. /**
  99. * The geometry instances rendered with this primitive. This may
  100. * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>
  101. * is <code>true</code> when the primitive is constructed.
  102. * <p>
  103. * Changing this property after the primitive is rendered has no effect.
  104. * </p>
  105. *
  106. * @readonly
  107. * @type {Array|GeometryInstance}
  108. *
  109. * @default undefined
  110. */
  111. this.geometryInstances = options.geometryInstances;
  112. this._hasPerInstanceColors = true;
  113. let appearance = options.appearance;
  114. if (!defined(appearance)) {
  115. appearance = new PolylineMaterialAppearance();
  116. }
  117. /**
  118. * The {@link Appearance} used to shade this primitive. Each geometry
  119. * instance is shaded with the same appearance. Some appearances, like
  120. * {@link PolylineColorAppearance} allow giving each instance unique
  121. * properties.
  122. *
  123. * @type Appearance
  124. *
  125. * @default undefined
  126. */
  127. this.appearance = appearance;
  128. /**
  129. * Determines if the primitive will be shown. This affects all geometry
  130. * instances in the primitive.
  131. *
  132. * @type {Boolean}
  133. *
  134. * @default true
  135. */
  136. this.show = defaultValue(options.show, true);
  137. /**
  138. * Determines whether terrain, 3D Tiles or both will be classified.
  139. *
  140. * @type {ClassificationType}
  141. *
  142. * @default ClassificationType.BOTH
  143. */
  144. this.classificationType = defaultValue(
  145. options.classificationType,
  146. ClassificationType.BOTH
  147. );
  148. /**
  149. * This property is for debugging only; it is not for production use nor is it optimized.
  150. * <p>
  151. * Draws the bounding sphere for each draw command in the primitive.
  152. * </p>
  153. *
  154. * @type {Boolean}
  155. *
  156. * @default false
  157. */
  158. this.debugShowBoundingVolume = defaultValue(
  159. options.debugShowBoundingVolume,
  160. false
  161. );
  162. // Shadow volume is shown by removing a discard in the shader, so this isn't toggleable.
  163. this._debugShowShadowVolume = defaultValue(
  164. options.debugShowShadowVolume,
  165. false
  166. );
  167. this._primitiveOptions = {
  168. geometryInstances: undefined,
  169. appearance: undefined,
  170. vertexCacheOptimize: false,
  171. interleave: defaultValue(options.interleave, false),
  172. releaseGeometryInstances: defaultValue(
  173. options.releaseGeometryInstances,
  174. true
  175. ),
  176. allowPicking: defaultValue(options.allowPicking, true),
  177. asynchronous: defaultValue(options.asynchronous, true),
  178. compressVertices: false,
  179. _createShaderProgramFunction: undefined,
  180. _createCommandsFunction: undefined,
  181. _updateAndQueueCommandsFunction: undefined,
  182. };
  183. // Used when inserting in an OrderedPrimitiveCollection
  184. this._zIndex = undefined;
  185. this._ready = false;
  186. this._readyPromise = defer();
  187. this._primitive = undefined;
  188. this._sp = undefined;
  189. this._sp2D = undefined;
  190. this._spMorph = undefined;
  191. this._renderState = getRenderState(false);
  192. this._renderState3DTiles = getRenderState(true);
  193. this._renderStateMorph = RenderState.fromCache({
  194. cull: {
  195. enabled: true,
  196. face: CullFace.FRONT, // Geometry is "inverted," so cull front when materials on volume instead of on terrain (morph)
  197. },
  198. depthTest: {
  199. enabled: true,
  200. },
  201. blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  202. depthMask: false,
  203. });
  204. }
  205. Object.defineProperties(GroundPolylinePrimitive.prototype, {
  206. /**
  207. * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
  208. *
  209. * @memberof GroundPolylinePrimitive.prototype
  210. *
  211. * @type {Boolean}
  212. * @readonly
  213. *
  214. * @default false
  215. */
  216. interleave: {
  217. get: function () {
  218. return this._primitiveOptions.interleave;
  219. },
  220. },
  221. /**
  222. * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  223. *
  224. * @memberof GroundPolylinePrimitive.prototype
  225. *
  226. * @type {Boolean}
  227. * @readonly
  228. *
  229. * @default true
  230. */
  231. releaseGeometryInstances: {
  232. get: function () {
  233. return this._primitiveOptions.releaseGeometryInstances;
  234. },
  235. },
  236. /**
  237. * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  238. *
  239. * @memberof GroundPolylinePrimitive.prototype
  240. *
  241. * @type {Boolean}
  242. * @readonly
  243. *
  244. * @default true
  245. */
  246. allowPicking: {
  247. get: function () {
  248. return this._primitiveOptions.allowPicking;
  249. },
  250. },
  251. /**
  252. * Determines if the geometry instances will be created and batched on a web worker.
  253. *
  254. * @memberof GroundPolylinePrimitive.prototype
  255. *
  256. * @type {Boolean}
  257. * @readonly
  258. *
  259. * @default true
  260. */
  261. asynchronous: {
  262. get: function () {
  263. return this._primitiveOptions.asynchronous;
  264. },
  265. },
  266. /**
  267. * Determines if the primitive is complete and ready to render. If this property is
  268. * true, the primitive will be rendered the next time that {@link GroundPolylinePrimitive#update}
  269. * is called.
  270. *
  271. * @memberof GroundPolylinePrimitive.prototype
  272. *
  273. * @type {Boolean}
  274. * @readonly
  275. */
  276. ready: {
  277. get: function () {
  278. return this._ready;
  279. },
  280. },
  281. /**
  282. * Gets a promise that resolves when the primitive is ready to render.
  283. * @memberof GroundPolylinePrimitive.prototype
  284. * @type {Promise.<GroundPolylinePrimitive>}
  285. * @readonly
  286. */
  287. readyPromise: {
  288. get: function () {
  289. return this._readyPromise.promise;
  290. },
  291. },
  292. /**
  293. * This property is for debugging only; it is not for production use nor is it optimized.
  294. * <p>
  295. * If true, draws the shadow volume for each geometry in the primitive.
  296. * </p>
  297. *
  298. * @memberof GroundPolylinePrimitive.prototype
  299. *
  300. * @type {Boolean}
  301. * @readonly
  302. *
  303. * @default false
  304. */
  305. debugShowShadowVolume: {
  306. get: function () {
  307. return this._debugShowShadowVolume;
  308. },
  309. },
  310. });
  311. /**
  312. * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the
  313. * GroundPolylinePrimitive synchronously.
  314. *
  315. * @returns {Promise<void>} A promise that will resolve once the terrain heights have been loaded.
  316. */
  317. GroundPolylinePrimitive.initializeTerrainHeights = function () {
  318. return ApproximateTerrainHeights.initialize();
  319. };
  320. function createShaderProgram(groundPolylinePrimitive, frameState, appearance) {
  321. const context = frameState.context;
  322. const primitive = groundPolylinePrimitive._primitive;
  323. const attributeLocations = primitive._attributeLocations;
  324. let vs = primitive._batchTable.getVertexShaderCallback()(
  325. PolylineShadowVolumeVS
  326. );
  327. vs = Primitive._appendShowToShader(primitive, vs);
  328. vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);
  329. vs = Primitive._modifyShaderPosition(
  330. groundPolylinePrimitive,
  331. vs,
  332. frameState.scene3DOnly
  333. );
  334. let vsMorph = primitive._batchTable.getVertexShaderCallback()(
  335. PolylineShadowVolumeMorphVS
  336. );
  337. vsMorph = Primitive._appendShowToShader(primitive, vsMorph);
  338. vsMorph = Primitive._appendDistanceDisplayConditionToShader(
  339. primitive,
  340. vsMorph
  341. );
  342. vsMorph = Primitive._modifyShaderPosition(
  343. groundPolylinePrimitive,
  344. vsMorph,
  345. frameState.scene3DOnly
  346. );
  347. // Access pick color from fragment shader.
  348. // Helps with varying budget.
  349. let fs = primitive._batchTable.getVertexShaderCallback()(
  350. PolylineShadowVolumeFS
  351. );
  352. const vsDefines = [
  353. `GLOBE_MINIMUM_ALTITUDE ${frameState.mapProjection.ellipsoid.minimumRadius.toFixed(
  354. 1
  355. )}`,
  356. ];
  357. let colorDefine = "";
  358. let materialShaderSource = "";
  359. if (defined(appearance.material)) {
  360. materialShaderSource = defined(appearance.material)
  361. ? appearance.material.shaderSource
  362. : "";
  363. // Check for use of v_width and v_polylineAngle in material shader
  364. // to determine whether these varyings should be active in the vertex shader.
  365. if (
  366. materialShaderSource.search(/varying\s+float\s+v_polylineAngle;/g) !== -1
  367. ) {
  368. vsDefines.push("ANGLE_VARYING");
  369. }
  370. if (materialShaderSource.search(/varying\s+float\s+v_width;/g) !== -1) {
  371. vsDefines.push("WIDTH_VARYING");
  372. }
  373. } else {
  374. colorDefine = "PER_INSTANCE_COLOR";
  375. }
  376. vsDefines.push(colorDefine);
  377. const fsDefines = groundPolylinePrimitive.debugShowShadowVolume
  378. ? ["DEBUG_SHOW_VOLUME", colorDefine]
  379. : [colorDefine];
  380. const vsColor3D = new ShaderSource({
  381. defines: vsDefines,
  382. sources: [vs],
  383. });
  384. const fsColor3D = new ShaderSource({
  385. defines: fsDefines,
  386. sources: [materialShaderSource, fs],
  387. });
  388. groundPolylinePrimitive._sp = ShaderProgram.replaceCache({
  389. context: context,
  390. shaderProgram: primitive._sp,
  391. vertexShaderSource: vsColor3D,
  392. fragmentShaderSource: fsColor3D,
  393. attributeLocations: attributeLocations,
  394. });
  395. // Derive 2D/CV
  396. let colorProgram2D = context.shaderCache.getDerivedShaderProgram(
  397. groundPolylinePrimitive._sp,
  398. "2dColor"
  399. );
  400. if (!defined(colorProgram2D)) {
  401. const vsColor2D = new ShaderSource({
  402. defines: vsDefines.concat(["COLUMBUS_VIEW_2D"]),
  403. sources: [vs],
  404. });
  405. colorProgram2D = context.shaderCache.createDerivedShaderProgram(
  406. groundPolylinePrimitive._sp,
  407. "2dColor",
  408. {
  409. context: context,
  410. shaderProgram: groundPolylinePrimitive._sp2D,
  411. vertexShaderSource: vsColor2D,
  412. fragmentShaderSource: fsColor3D,
  413. attributeLocations: attributeLocations,
  414. }
  415. );
  416. }
  417. groundPolylinePrimitive._sp2D = colorProgram2D;
  418. // Derive Morph
  419. let colorProgramMorph = context.shaderCache.getDerivedShaderProgram(
  420. groundPolylinePrimitive._sp,
  421. "MorphColor"
  422. );
  423. if (!defined(colorProgramMorph)) {
  424. const vsColorMorph = new ShaderSource({
  425. defines: vsDefines.concat([
  426. `MAX_TERRAIN_HEIGHT ${ApproximateTerrainHeights._defaultMaxTerrainHeight.toFixed(
  427. 1
  428. )}`,
  429. ]),
  430. sources: [vsMorph],
  431. });
  432. fs = primitive._batchTable.getVertexShaderCallback()(
  433. PolylineShadowVolumeMorphFS
  434. );
  435. const fsColorMorph = new ShaderSource({
  436. defines: fsDefines,
  437. sources: [materialShaderSource, fs],
  438. });
  439. colorProgramMorph = context.shaderCache.createDerivedShaderProgram(
  440. groundPolylinePrimitive._sp,
  441. "MorphColor",
  442. {
  443. context: context,
  444. shaderProgram: groundPolylinePrimitive._spMorph,
  445. vertexShaderSource: vsColorMorph,
  446. fragmentShaderSource: fsColorMorph,
  447. attributeLocations: attributeLocations,
  448. }
  449. );
  450. }
  451. groundPolylinePrimitive._spMorph = colorProgramMorph;
  452. }
  453. function getRenderState(mask3DTiles) {
  454. return RenderState.fromCache({
  455. cull: {
  456. enabled: true, // prevent double-draw. Geometry is "inverted" (reversed winding order) so we're drawing backfaces.
  457. },
  458. blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  459. depthMask: false,
  460. stencilTest: {
  461. enabled: mask3DTiles,
  462. frontFunction: StencilFunction.EQUAL,
  463. frontOperation: {
  464. fail: StencilOperation.KEEP,
  465. zFail: StencilOperation.KEEP,
  466. zPass: StencilOperation.KEEP,
  467. },
  468. backFunction: StencilFunction.EQUAL,
  469. backOperation: {
  470. fail: StencilOperation.KEEP,
  471. zFail: StencilOperation.KEEP,
  472. zPass: StencilOperation.KEEP,
  473. },
  474. reference: StencilConstants.CESIUM_3D_TILE_MASK,
  475. mask: StencilConstants.CESIUM_3D_TILE_MASK,
  476. },
  477. });
  478. }
  479. function createCommands(
  480. groundPolylinePrimitive,
  481. appearance,
  482. material,
  483. translucent,
  484. colorCommands,
  485. pickCommands
  486. ) {
  487. const primitive = groundPolylinePrimitive._primitive;
  488. const length = primitive._va.length;
  489. colorCommands.length = length;
  490. pickCommands.length = length;
  491. const isPolylineColorAppearance =
  492. appearance instanceof PolylineColorAppearance;
  493. const materialUniforms = isPolylineColorAppearance ? {} : material._uniforms;
  494. const uniformMap = primitive._batchTable.getUniformMapCallback()(
  495. materialUniforms
  496. );
  497. for (let i = 0; i < length; i++) {
  498. const vertexArray = primitive._va[i];
  499. let command = colorCommands[i];
  500. if (!defined(command)) {
  501. command = colorCommands[i] = new DrawCommand({
  502. owner: groundPolylinePrimitive,
  503. primitiveType: primitive._primitiveType,
  504. });
  505. }
  506. command.vertexArray = vertexArray;
  507. command.renderState = groundPolylinePrimitive._renderState;
  508. command.shaderProgram = groundPolylinePrimitive._sp;
  509. command.uniformMap = uniformMap;
  510. command.pass = Pass.TERRAIN_CLASSIFICATION;
  511. command.pickId = "czm_batchTable_pickColor(v_endPlaneNormalEcAndBatchId.w)";
  512. const derivedTilesetCommand = DrawCommand.shallowClone(
  513. command,
  514. command.derivedCommands.tileset
  515. );
  516. derivedTilesetCommand.renderState =
  517. groundPolylinePrimitive._renderState3DTiles;
  518. derivedTilesetCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  519. command.derivedCommands.tileset = derivedTilesetCommand;
  520. // derive for 2D
  521. const derived2DCommand = DrawCommand.shallowClone(
  522. command,
  523. command.derivedCommands.color2D
  524. );
  525. derived2DCommand.shaderProgram = groundPolylinePrimitive._sp2D;
  526. command.derivedCommands.color2D = derived2DCommand;
  527. const derived2DTilesetCommand = DrawCommand.shallowClone(
  528. derivedTilesetCommand,
  529. derivedTilesetCommand.derivedCommands.color2D
  530. );
  531. derived2DTilesetCommand.shaderProgram = groundPolylinePrimitive._sp2D;
  532. derivedTilesetCommand.derivedCommands.color2D = derived2DTilesetCommand;
  533. // derive for Morph
  534. const derivedMorphCommand = DrawCommand.shallowClone(
  535. command,
  536. command.derivedCommands.colorMorph
  537. );
  538. derivedMorphCommand.renderState = groundPolylinePrimitive._renderStateMorph;
  539. derivedMorphCommand.shaderProgram = groundPolylinePrimitive._spMorph;
  540. derivedMorphCommand.pickId = "czm_batchTable_pickColor(v_batchId)";
  541. command.derivedCommands.colorMorph = derivedMorphCommand;
  542. }
  543. }
  544. function updateAndQueueCommand(
  545. groundPolylinePrimitive,
  546. command,
  547. frameState,
  548. modelMatrix,
  549. cull,
  550. boundingVolume,
  551. debugShowBoundingVolume
  552. ) {
  553. // Use derived appearance command for morph and 2D
  554. if (frameState.mode === SceneMode.MORPHING) {
  555. command = command.derivedCommands.colorMorph;
  556. } else if (frameState.mode !== SceneMode.SCENE3D) {
  557. command = command.derivedCommands.color2D;
  558. }
  559. command.modelMatrix = modelMatrix;
  560. command.boundingVolume = boundingVolume;
  561. command.cull = cull;
  562. command.debugShowBoundingVolume = debugShowBoundingVolume;
  563. frameState.commandList.push(command);
  564. }
  565. function updateAndQueueCommands(
  566. groundPolylinePrimitive,
  567. frameState,
  568. colorCommands,
  569. pickCommands,
  570. modelMatrix,
  571. cull,
  572. debugShowBoundingVolume
  573. ) {
  574. const primitive = groundPolylinePrimitive._primitive;
  575. Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); // Expected to be identity - GroundPrimitives don't support other model matrices
  576. let boundingSpheres;
  577. if (frameState.mode === SceneMode.SCENE3D) {
  578. boundingSpheres = primitive._boundingSphereWC;
  579. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  580. boundingSpheres = primitive._boundingSphereCV;
  581. } else if (
  582. frameState.mode === SceneMode.SCENE2D &&
  583. defined(primitive._boundingSphere2D)
  584. ) {
  585. boundingSpheres = primitive._boundingSphere2D;
  586. } else if (defined(primitive._boundingSphereMorph)) {
  587. boundingSpheres = primitive._boundingSphereMorph;
  588. }
  589. const morphing = frameState.mode === SceneMode.MORPHING;
  590. const classificationType = groundPolylinePrimitive.classificationType;
  591. const queueTerrainCommands =
  592. classificationType !== ClassificationType.CESIUM_3D_TILE;
  593. const queue3DTilesCommands =
  594. classificationType !== ClassificationType.TERRAIN && !morphing;
  595. let command;
  596. const passes = frameState.passes;
  597. if (passes.render || (passes.pick && primitive.allowPicking)) {
  598. const colorLength = colorCommands.length;
  599. for (let j = 0; j < colorLength; ++j) {
  600. const boundingVolume = boundingSpheres[j];
  601. if (queueTerrainCommands) {
  602. command = colorCommands[j];
  603. updateAndQueueCommand(
  604. groundPolylinePrimitive,
  605. command,
  606. frameState,
  607. modelMatrix,
  608. cull,
  609. boundingVolume,
  610. debugShowBoundingVolume
  611. );
  612. }
  613. if (queue3DTilesCommands) {
  614. command = colorCommands[j].derivedCommands.tileset;
  615. updateAndQueueCommand(
  616. groundPolylinePrimitive,
  617. command,
  618. frameState,
  619. modelMatrix,
  620. cull,
  621. boundingVolume,
  622. debugShowBoundingVolume
  623. );
  624. }
  625. }
  626. }
  627. }
  628. /**
  629. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  630. * get the draw commands needed to render this primitive.
  631. * <p>
  632. * Do not call this function directly. This is documented just to
  633. * list the exceptions that may be propagated when the scene is rendered:
  634. * </p>
  635. *
  636. * @exception {DeveloperError} For synchronous GroundPolylinePrimitives, you must call GroundPolylinePrimitives.initializeTerrainHeights() and wait for the returned promise to resolve.
  637. * @exception {DeveloperError} All GeometryInstances must have color attributes to use PolylineColorAppearance with GroundPolylinePrimitive.
  638. */
  639. GroundPolylinePrimitive.prototype.update = function (frameState) {
  640. if (!defined(this._primitive) && !defined(this.geometryInstances)) {
  641. return;
  642. }
  643. if (!ApproximateTerrainHeights.initialized) {
  644. //>>includeStart('debug', pragmas.debug);
  645. if (!this.asynchronous) {
  646. throw new DeveloperError(
  647. "For synchronous GroundPolylinePrimitives, you must call GroundPolylinePrimitives.initializeTerrainHeights() and wait for the returned promise to resolve."
  648. );
  649. }
  650. //>>includeEnd('debug');
  651. GroundPolylinePrimitive.initializeTerrainHeights();
  652. return;
  653. }
  654. let i;
  655. const that = this;
  656. const primitiveOptions = this._primitiveOptions;
  657. if (!defined(this._primitive)) {
  658. const geometryInstances = Array.isArray(this.geometryInstances)
  659. ? this.geometryInstances
  660. : [this.geometryInstances];
  661. const geometryInstancesLength = geometryInstances.length;
  662. const groundInstances = new Array(geometryInstancesLength);
  663. let attributes;
  664. // Check if each instance has a color attribute.
  665. for (i = 0; i < geometryInstancesLength; ++i) {
  666. attributes = geometryInstances[i].attributes;
  667. if (!defined(attributes) || !defined(attributes.color)) {
  668. this._hasPerInstanceColors = false;
  669. break;
  670. }
  671. }
  672. for (i = 0; i < geometryInstancesLength; ++i) {
  673. const geometryInstance = geometryInstances[i];
  674. attributes = {};
  675. const instanceAttributes = geometryInstance.attributes;
  676. for (const attributeKey in instanceAttributes) {
  677. if (instanceAttributes.hasOwnProperty(attributeKey)) {
  678. attributes[attributeKey] = instanceAttributes[attributeKey];
  679. }
  680. }
  681. // Automatically create line width attribute if not already given
  682. if (!defined(attributes.width)) {
  683. attributes.width = new GeometryInstanceAttribute({
  684. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  685. componentsPerAttribute: 1.0,
  686. value: [geometryInstance.geometry.width],
  687. });
  688. }
  689. // Update each geometry for framestate.scene3DOnly = true and projection
  690. geometryInstance.geometry._scene3DOnly = frameState.scene3DOnly;
  691. GroundPolylineGeometry.setProjectionAndEllipsoid(
  692. geometryInstance.geometry,
  693. frameState.mapProjection
  694. );
  695. groundInstances[i] = new GeometryInstance({
  696. geometry: geometryInstance.geometry,
  697. attributes: attributes,
  698. id: geometryInstance.id,
  699. pickPrimitive: that,
  700. });
  701. }
  702. primitiveOptions.geometryInstances = groundInstances;
  703. primitiveOptions.appearance = this.appearance;
  704. primitiveOptions._createShaderProgramFunction = function (
  705. primitive,
  706. frameState,
  707. appearance
  708. ) {
  709. createShaderProgram(that, frameState, appearance);
  710. };
  711. primitiveOptions._createCommandsFunction = function (
  712. primitive,
  713. appearance,
  714. material,
  715. translucent,
  716. twoPasses,
  717. colorCommands,
  718. pickCommands
  719. ) {
  720. createCommands(
  721. that,
  722. appearance,
  723. material,
  724. translucent,
  725. colorCommands,
  726. pickCommands
  727. );
  728. };
  729. primitiveOptions._updateAndQueueCommandsFunction = function (
  730. primitive,
  731. frameState,
  732. colorCommands,
  733. pickCommands,
  734. modelMatrix,
  735. cull,
  736. debugShowBoundingVolume,
  737. twoPasses
  738. ) {
  739. updateAndQueueCommands(
  740. that,
  741. frameState,
  742. colorCommands,
  743. pickCommands,
  744. modelMatrix,
  745. cull,
  746. debugShowBoundingVolume
  747. );
  748. };
  749. this._primitive = new Primitive(primitiveOptions);
  750. this._primitive.readyPromise.then(function (primitive) {
  751. that._ready = true;
  752. if (that.releaseGeometryInstances) {
  753. that.geometryInstances = undefined;
  754. }
  755. const error = primitive._error;
  756. if (!defined(error)) {
  757. that._readyPromise.resolve(that);
  758. } else {
  759. that._readyPromise.reject(error);
  760. }
  761. });
  762. }
  763. if (
  764. this.appearance instanceof PolylineColorAppearance &&
  765. !this._hasPerInstanceColors
  766. ) {
  767. throw new DeveloperError(
  768. "All GeometryInstances must have color attributes to use PolylineColorAppearance with GroundPolylinePrimitive."
  769. );
  770. }
  771. this._primitive.appearance = this.appearance;
  772. this._primitive.show = this.show;
  773. this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
  774. this._primitive.update(frameState);
  775. };
  776. /**
  777. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  778. *
  779. * @param {*} id The id of the {@link GeometryInstance}.
  780. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id.
  781. *
  782. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  783. *
  784. * @example
  785. * const attributes = primitive.getGeometryInstanceAttributes('an id');
  786. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  787. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  788. */
  789. GroundPolylinePrimitive.prototype.getGeometryInstanceAttributes = function (
  790. id
  791. ) {
  792. //>>includeStart('debug', pragmas.debug);
  793. if (!defined(this._primitive)) {
  794. throw new DeveloperError(
  795. "must call update before calling getGeometryInstanceAttributes"
  796. );
  797. }
  798. //>>includeEnd('debug');
  799. return this._primitive.getGeometryInstanceAttributes(id);
  800. };
  801. /**
  802. * Checks if the given Scene supports GroundPolylinePrimitives.
  803. * GroundPolylinePrimitives require support for the WEBGL_depth_texture extension.
  804. *
  805. * @param {Scene} scene The current scene.
  806. * @returns {Boolean} Whether or not the current scene supports GroundPolylinePrimitives.
  807. */
  808. GroundPolylinePrimitive.isSupported = function (scene) {
  809. return scene.frameState.context.depthTexture;
  810. };
  811. /**
  812. * Returns true if this object was destroyed; otherwise, false.
  813. * <p>
  814. * If this object was destroyed, it should not be used; calling any function other than
  815. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  816. * </p>
  817. *
  818. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  819. *
  820. * @see GroundPolylinePrimitive#destroy
  821. */
  822. GroundPolylinePrimitive.prototype.isDestroyed = function () {
  823. return false;
  824. };
  825. /**
  826. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  827. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  828. * <p>
  829. * Once an object is destroyed, it should not be used; calling any function other than
  830. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  831. * assign the return value (<code>undefined</code>) to the object as done in the example.
  832. * </p>
  833. *
  834. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  835. *
  836. * @example
  837. * e = e && e.destroy();
  838. *
  839. * @see GroundPolylinePrimitive#isDestroyed
  840. */
  841. GroundPolylinePrimitive.prototype.destroy = function () {
  842. this._primitive = this._primitive && this._primitive.destroy();
  843. this._sp = this._sp && this._sp.destroy();
  844. // Derived programs, destroyed above if they existed.
  845. this._sp2D = undefined;
  846. this._spMorph = undefined;
  847. return destroyObject(this);
  848. };
  849. export default GroundPolylinePrimitive;