GroundPolylinePrimitive.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import defaultValue from "../Core/defaultValue.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import DeveloperError from "../Core/DeveloperError.js";
  7. import deprecationWarning from "../Core/deprecationWarning.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. const groundPolylinePrimitive = this;
  187. // This is here for backwards compatibility. This promise wrapper can be removed once readyPromise is removed.
  188. this._readyPromise = new Promise((resolve, reject) => {
  189. groundPolylinePrimitive._completeLoad = () => {
  190. this._ready = true;
  191. if (this.releaseGeometryInstances) {
  192. this.geometryInstances = undefined;
  193. }
  194. const error = this._error;
  195. if (!defined(error)) {
  196. resolve(this);
  197. } else {
  198. reject(error);
  199. }
  200. };
  201. });
  202. this._primitive = undefined;
  203. this._sp = undefined;
  204. this._sp2D = undefined;
  205. this._spMorph = undefined;
  206. this._renderState = getRenderState(false);
  207. this._renderState3DTiles = getRenderState(true);
  208. this._renderStateMorph = RenderState.fromCache({
  209. cull: {
  210. enabled: true,
  211. face: CullFace.FRONT, // Geometry is "inverted," so cull front when materials on volume instead of on terrain (morph)
  212. },
  213. depthTest: {
  214. enabled: true,
  215. },
  216. blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  217. depthMask: false,
  218. });
  219. }
  220. Object.defineProperties(GroundPolylinePrimitive.prototype, {
  221. /**
  222. * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
  223. *
  224. * @memberof GroundPolylinePrimitive.prototype
  225. *
  226. * @type {boolean}
  227. * @readonly
  228. *
  229. * @default false
  230. */
  231. interleave: {
  232. get: function () {
  233. return this._primitiveOptions.interleave;
  234. },
  235. },
  236. /**
  237. * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  238. *
  239. * @memberof GroundPolylinePrimitive.prototype
  240. *
  241. * @type {boolean}
  242. * @readonly
  243. *
  244. * @default true
  245. */
  246. releaseGeometryInstances: {
  247. get: function () {
  248. return this._primitiveOptions.releaseGeometryInstances;
  249. },
  250. },
  251. /**
  252. * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  253. *
  254. * @memberof GroundPolylinePrimitive.prototype
  255. *
  256. * @type {boolean}
  257. * @readonly
  258. *
  259. * @default true
  260. */
  261. allowPicking: {
  262. get: function () {
  263. return this._primitiveOptions.allowPicking;
  264. },
  265. },
  266. /**
  267. * Determines if the geometry instances will be created and batched on a web worker.
  268. *
  269. * @memberof GroundPolylinePrimitive.prototype
  270. *
  271. * @type {boolean}
  272. * @readonly
  273. *
  274. * @default true
  275. */
  276. asynchronous: {
  277. get: function () {
  278. return this._primitiveOptions.asynchronous;
  279. },
  280. },
  281. /**
  282. * Determines if the primitive is complete and ready to render. If this property is
  283. * true, the primitive will be rendered the next time that {@link GroundPolylinePrimitive#update}
  284. * is called.
  285. *
  286. * @memberof GroundPolylinePrimitive.prototype
  287. *
  288. * @type {boolean}
  289. * @readonly
  290. */
  291. ready: {
  292. get: function () {
  293. return this._ready;
  294. },
  295. },
  296. /**
  297. * Gets a promise that resolves when the primitive is ready to render.
  298. * @memberof GroundPolylinePrimitive.prototype
  299. * @type {Promise<GroundPolylinePrimitive>}
  300. * @readonly
  301. * @deprecated
  302. */
  303. readyPromise: {
  304. get: function () {
  305. deprecationWarning(
  306. "GroundPolylinePrimitive.readyPromise",
  307. "GroundPolylinePrimitive.readyPromise was deprecated in CesiumJS 1.104. It will be removed in 1.107. Wait for GroundPolylinePrimitive.ready to return true instead."
  308. );
  309. return this._readyPromise;
  310. },
  311. },
  312. /**
  313. * This property is for debugging only; it is not for production use nor is it optimized.
  314. * <p>
  315. * If true, draws the shadow volume for each geometry in the primitive.
  316. * </p>
  317. *
  318. * @memberof GroundPolylinePrimitive.prototype
  319. *
  320. * @type {boolean}
  321. * @readonly
  322. *
  323. * @default false
  324. */
  325. debugShowShadowVolume: {
  326. get: function () {
  327. return this._debugShowShadowVolume;
  328. },
  329. },
  330. });
  331. /**
  332. * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the
  333. * GroundPolylinePrimitive synchronously.
  334. *
  335. * @returns {Promise<void>} A promise that will resolve once the terrain heights have been loaded.
  336. */
  337. GroundPolylinePrimitive.initializeTerrainHeights = function () {
  338. return ApproximateTerrainHeights.initialize();
  339. };
  340. function createShaderProgram(groundPolylinePrimitive, frameState, appearance) {
  341. const context = frameState.context;
  342. const primitive = groundPolylinePrimitive._primitive;
  343. const attributeLocations = primitive._attributeLocations;
  344. let vs = primitive._batchTable.getVertexShaderCallback()(
  345. PolylineShadowVolumeVS
  346. );
  347. vs = Primitive._appendShowToShader(primitive, vs);
  348. vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);
  349. vs = Primitive._modifyShaderPosition(
  350. groundPolylinePrimitive,
  351. vs,
  352. frameState.scene3DOnly
  353. );
  354. let vsMorph = primitive._batchTable.getVertexShaderCallback()(
  355. PolylineShadowVolumeMorphVS
  356. );
  357. vsMorph = Primitive._appendShowToShader(primitive, vsMorph);
  358. vsMorph = Primitive._appendDistanceDisplayConditionToShader(
  359. primitive,
  360. vsMorph
  361. );
  362. vsMorph = Primitive._modifyShaderPosition(
  363. groundPolylinePrimitive,
  364. vsMorph,
  365. frameState.scene3DOnly
  366. );
  367. // Access pick color from fragment shader.
  368. // Helps with varying budget.
  369. let fs = primitive._batchTable.getVertexShaderCallback()(
  370. PolylineShadowVolumeFS
  371. );
  372. const vsDefines = [
  373. `GLOBE_MINIMUM_ALTITUDE ${frameState.mapProjection.ellipsoid.minimumRadius.toFixed(
  374. 1
  375. )}`,
  376. ];
  377. let colorDefine = "";
  378. let materialShaderSource = "";
  379. if (defined(appearance.material)) {
  380. materialShaderSource = defined(appearance.material)
  381. ? appearance.material.shaderSource
  382. : "";
  383. // Check for use of v_width and v_polylineAngle in material shader
  384. // to determine whether these varyings should be active in the vertex shader.
  385. if (materialShaderSource.search(/in\s+float\s+v_polylineAngle;/g) !== -1) {
  386. vsDefines.push("ANGLE_VARYING");
  387. }
  388. if (materialShaderSource.search(/in\s+float\s+v_width;/g) !== -1) {
  389. vsDefines.push("WIDTH_VARYING");
  390. }
  391. } else {
  392. colorDefine = "PER_INSTANCE_COLOR";
  393. }
  394. vsDefines.push(colorDefine);
  395. const fsDefines = groundPolylinePrimitive.debugShowShadowVolume
  396. ? ["DEBUG_SHOW_VOLUME", colorDefine]
  397. : [colorDefine];
  398. const vsColor3D = new ShaderSource({
  399. defines: vsDefines,
  400. sources: [vs],
  401. });
  402. const fsColor3D = new ShaderSource({
  403. defines: fsDefines,
  404. sources: [materialShaderSource, fs],
  405. });
  406. groundPolylinePrimitive._sp = ShaderProgram.replaceCache({
  407. context: context,
  408. shaderProgram: primitive._sp,
  409. vertexShaderSource: vsColor3D,
  410. fragmentShaderSource: fsColor3D,
  411. attributeLocations: attributeLocations,
  412. });
  413. // Derive 2D/CV
  414. let colorProgram2D = context.shaderCache.getDerivedShaderProgram(
  415. groundPolylinePrimitive._sp,
  416. "2dColor"
  417. );
  418. if (!defined(colorProgram2D)) {
  419. const vsColor2D = new ShaderSource({
  420. defines: vsDefines.concat(["COLUMBUS_VIEW_2D"]),
  421. sources: [vs],
  422. });
  423. colorProgram2D = context.shaderCache.createDerivedShaderProgram(
  424. groundPolylinePrimitive._sp,
  425. "2dColor",
  426. {
  427. context: context,
  428. shaderProgram: groundPolylinePrimitive._sp2D,
  429. vertexShaderSource: vsColor2D,
  430. fragmentShaderSource: fsColor3D,
  431. attributeLocations: attributeLocations,
  432. }
  433. );
  434. }
  435. groundPolylinePrimitive._sp2D = colorProgram2D;
  436. // Derive Morph
  437. let colorProgramMorph = context.shaderCache.getDerivedShaderProgram(
  438. groundPolylinePrimitive._sp,
  439. "MorphColor"
  440. );
  441. if (!defined(colorProgramMorph)) {
  442. const vsColorMorph = new ShaderSource({
  443. defines: vsDefines.concat([
  444. `MAX_TERRAIN_HEIGHT ${ApproximateTerrainHeights._defaultMaxTerrainHeight.toFixed(
  445. 1
  446. )}`,
  447. ]),
  448. sources: [vsMorph],
  449. });
  450. fs = primitive._batchTable.getVertexShaderCallback()(
  451. PolylineShadowVolumeMorphFS
  452. );
  453. const fsColorMorph = new ShaderSource({
  454. defines: fsDefines,
  455. sources: [materialShaderSource, fs],
  456. });
  457. colorProgramMorph = context.shaderCache.createDerivedShaderProgram(
  458. groundPolylinePrimitive._sp,
  459. "MorphColor",
  460. {
  461. context: context,
  462. shaderProgram: groundPolylinePrimitive._spMorph,
  463. vertexShaderSource: vsColorMorph,
  464. fragmentShaderSource: fsColorMorph,
  465. attributeLocations: attributeLocations,
  466. }
  467. );
  468. }
  469. groundPolylinePrimitive._spMorph = colorProgramMorph;
  470. }
  471. function getRenderState(mask3DTiles) {
  472. return RenderState.fromCache({
  473. cull: {
  474. enabled: true, // prevent double-draw. Geometry is "inverted" (reversed winding order) so we're drawing backfaces.
  475. },
  476. blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  477. depthMask: false,
  478. stencilTest: {
  479. enabled: mask3DTiles,
  480. frontFunction: StencilFunction.EQUAL,
  481. frontOperation: {
  482. fail: StencilOperation.KEEP,
  483. zFail: StencilOperation.KEEP,
  484. zPass: StencilOperation.KEEP,
  485. },
  486. backFunction: StencilFunction.EQUAL,
  487. backOperation: {
  488. fail: StencilOperation.KEEP,
  489. zFail: StencilOperation.KEEP,
  490. zPass: StencilOperation.KEEP,
  491. },
  492. reference: StencilConstants.CESIUM_3D_TILE_MASK,
  493. mask: StencilConstants.CESIUM_3D_TILE_MASK,
  494. },
  495. });
  496. }
  497. function createCommands(
  498. groundPolylinePrimitive,
  499. appearance,
  500. material,
  501. translucent,
  502. colorCommands,
  503. pickCommands
  504. ) {
  505. const primitive = groundPolylinePrimitive._primitive;
  506. const length = primitive._va.length;
  507. colorCommands.length = length;
  508. pickCommands.length = length;
  509. const isPolylineColorAppearance =
  510. appearance instanceof PolylineColorAppearance;
  511. const materialUniforms = isPolylineColorAppearance ? {} : material._uniforms;
  512. const uniformMap = primitive._batchTable.getUniformMapCallback()(
  513. materialUniforms
  514. );
  515. for (let i = 0; i < length; i++) {
  516. const vertexArray = primitive._va[i];
  517. let command = colorCommands[i];
  518. if (!defined(command)) {
  519. command = colorCommands[i] = new DrawCommand({
  520. owner: groundPolylinePrimitive,
  521. primitiveType: primitive._primitiveType,
  522. });
  523. }
  524. command.vertexArray = vertexArray;
  525. command.renderState = groundPolylinePrimitive._renderState;
  526. command.shaderProgram = groundPolylinePrimitive._sp;
  527. command.uniformMap = uniformMap;
  528. command.pass = Pass.TERRAIN_CLASSIFICATION;
  529. command.pickId = "czm_batchTable_pickColor(v_endPlaneNormalEcAndBatchId.w)";
  530. const derivedTilesetCommand = DrawCommand.shallowClone(
  531. command,
  532. command.derivedCommands.tileset
  533. );
  534. derivedTilesetCommand.renderState =
  535. groundPolylinePrimitive._renderState3DTiles;
  536. derivedTilesetCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  537. command.derivedCommands.tileset = derivedTilesetCommand;
  538. // derive for 2D
  539. const derived2DCommand = DrawCommand.shallowClone(
  540. command,
  541. command.derivedCommands.color2D
  542. );
  543. derived2DCommand.shaderProgram = groundPolylinePrimitive._sp2D;
  544. command.derivedCommands.color2D = derived2DCommand;
  545. const derived2DTilesetCommand = DrawCommand.shallowClone(
  546. derivedTilesetCommand,
  547. derivedTilesetCommand.derivedCommands.color2D
  548. );
  549. derived2DTilesetCommand.shaderProgram = groundPolylinePrimitive._sp2D;
  550. derivedTilesetCommand.derivedCommands.color2D = derived2DTilesetCommand;
  551. // derive for Morph
  552. const derivedMorphCommand = DrawCommand.shallowClone(
  553. command,
  554. command.derivedCommands.colorMorph
  555. );
  556. derivedMorphCommand.renderState = groundPolylinePrimitive._renderStateMorph;
  557. derivedMorphCommand.shaderProgram = groundPolylinePrimitive._spMorph;
  558. derivedMorphCommand.pickId = "czm_batchTable_pickColor(v_batchId)";
  559. command.derivedCommands.colorMorph = derivedMorphCommand;
  560. }
  561. }
  562. function updateAndQueueCommand(
  563. groundPolylinePrimitive,
  564. command,
  565. frameState,
  566. modelMatrix,
  567. cull,
  568. boundingVolume,
  569. debugShowBoundingVolume
  570. ) {
  571. // Use derived appearance command for morph and 2D
  572. if (frameState.mode === SceneMode.MORPHING) {
  573. command = command.derivedCommands.colorMorph;
  574. } else if (frameState.mode !== SceneMode.SCENE3D) {
  575. command = command.derivedCommands.color2D;
  576. }
  577. command.modelMatrix = modelMatrix;
  578. command.boundingVolume = boundingVolume;
  579. command.cull = cull;
  580. command.debugShowBoundingVolume = debugShowBoundingVolume;
  581. frameState.commandList.push(command);
  582. }
  583. function updateAndQueueCommands(
  584. groundPolylinePrimitive,
  585. frameState,
  586. colorCommands,
  587. pickCommands,
  588. modelMatrix,
  589. cull,
  590. debugShowBoundingVolume
  591. ) {
  592. const primitive = groundPolylinePrimitive._primitive;
  593. Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); // Expected to be identity - GroundPrimitives don't support other model matrices
  594. let boundingSpheres;
  595. if (frameState.mode === SceneMode.SCENE3D) {
  596. boundingSpheres = primitive._boundingSphereWC;
  597. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  598. boundingSpheres = primitive._boundingSphereCV;
  599. } else if (
  600. frameState.mode === SceneMode.SCENE2D &&
  601. defined(primitive._boundingSphere2D)
  602. ) {
  603. boundingSpheres = primitive._boundingSphere2D;
  604. } else if (defined(primitive._boundingSphereMorph)) {
  605. boundingSpheres = primitive._boundingSphereMorph;
  606. }
  607. const morphing = frameState.mode === SceneMode.MORPHING;
  608. const classificationType = groundPolylinePrimitive.classificationType;
  609. const queueTerrainCommands =
  610. classificationType !== ClassificationType.CESIUM_3D_TILE;
  611. const queue3DTilesCommands =
  612. classificationType !== ClassificationType.TERRAIN && !morphing;
  613. let command;
  614. const passes = frameState.passes;
  615. if (passes.render || (passes.pick && primitive.allowPicking)) {
  616. const colorLength = colorCommands.length;
  617. for (let j = 0; j < colorLength; ++j) {
  618. const boundingVolume = boundingSpheres[j];
  619. if (queueTerrainCommands) {
  620. command = colorCommands[j];
  621. updateAndQueueCommand(
  622. groundPolylinePrimitive,
  623. command,
  624. frameState,
  625. modelMatrix,
  626. cull,
  627. boundingVolume,
  628. debugShowBoundingVolume
  629. );
  630. }
  631. if (queue3DTilesCommands) {
  632. command = colorCommands[j].derivedCommands.tileset;
  633. updateAndQueueCommand(
  634. groundPolylinePrimitive,
  635. command,
  636. frameState,
  637. modelMatrix,
  638. cull,
  639. boundingVolume,
  640. debugShowBoundingVolume
  641. );
  642. }
  643. }
  644. }
  645. }
  646. /**
  647. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  648. * get the draw commands needed to render this primitive.
  649. * <p>
  650. * Do not call this function directly. This is documented just to
  651. * list the exceptions that may be propagated when the scene is rendered:
  652. * </p>
  653. *
  654. * @exception {DeveloperError} For synchronous GroundPolylinePrimitives, you must call GroundPolylinePrimitives.initializeTerrainHeights() and wait for the returned promise to resolve.
  655. * @exception {DeveloperError} All GeometryInstances must have color attributes to use PolylineColorAppearance with GroundPolylinePrimitive.
  656. */
  657. GroundPolylinePrimitive.prototype.update = function (frameState) {
  658. if (!defined(this._primitive) && !defined(this.geometryInstances)) {
  659. return;
  660. }
  661. if (!ApproximateTerrainHeights.initialized) {
  662. //>>includeStart('debug', pragmas.debug);
  663. if (!this.asynchronous) {
  664. throw new DeveloperError(
  665. "For synchronous GroundPolylinePrimitives, you must call GroundPolylinePrimitives.initializeTerrainHeights() and wait for the returned promise to resolve."
  666. );
  667. }
  668. //>>includeEnd('debug');
  669. GroundPolylinePrimitive.initializeTerrainHeights();
  670. return;
  671. }
  672. let i;
  673. const that = this;
  674. const primitiveOptions = this._primitiveOptions;
  675. if (!defined(this._primitive)) {
  676. const geometryInstances = Array.isArray(this.geometryInstances)
  677. ? this.geometryInstances
  678. : [this.geometryInstances];
  679. const geometryInstancesLength = geometryInstances.length;
  680. const groundInstances = new Array(geometryInstancesLength);
  681. let attributes;
  682. // Check if each instance has a color attribute.
  683. for (i = 0; i < geometryInstancesLength; ++i) {
  684. attributes = geometryInstances[i].attributes;
  685. if (!defined(attributes) || !defined(attributes.color)) {
  686. this._hasPerInstanceColors = false;
  687. break;
  688. }
  689. }
  690. for (i = 0; i < geometryInstancesLength; ++i) {
  691. const geometryInstance = geometryInstances[i];
  692. attributes = {};
  693. const instanceAttributes = geometryInstance.attributes;
  694. for (const attributeKey in instanceAttributes) {
  695. if (instanceAttributes.hasOwnProperty(attributeKey)) {
  696. attributes[attributeKey] = instanceAttributes[attributeKey];
  697. }
  698. }
  699. // Automatically create line width attribute if not already given
  700. if (!defined(attributes.width)) {
  701. attributes.width = new GeometryInstanceAttribute({
  702. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  703. componentsPerAttribute: 1.0,
  704. value: [geometryInstance.geometry.width],
  705. });
  706. }
  707. // Update each geometry for framestate.scene3DOnly = true and projection
  708. geometryInstance.geometry._scene3DOnly = frameState.scene3DOnly;
  709. GroundPolylineGeometry.setProjectionAndEllipsoid(
  710. geometryInstance.geometry,
  711. frameState.mapProjection
  712. );
  713. groundInstances[i] = new GeometryInstance({
  714. geometry: geometryInstance.geometry,
  715. attributes: attributes,
  716. id: geometryInstance.id,
  717. pickPrimitive: that,
  718. });
  719. }
  720. primitiveOptions.geometryInstances = groundInstances;
  721. primitiveOptions.appearance = this.appearance;
  722. primitiveOptions._createShaderProgramFunction = function (
  723. primitive,
  724. frameState,
  725. appearance
  726. ) {
  727. createShaderProgram(that, frameState, appearance);
  728. };
  729. primitiveOptions._createCommandsFunction = function (
  730. primitive,
  731. appearance,
  732. material,
  733. translucent,
  734. twoPasses,
  735. colorCommands,
  736. pickCommands
  737. ) {
  738. createCommands(
  739. that,
  740. appearance,
  741. material,
  742. translucent,
  743. colorCommands,
  744. pickCommands
  745. );
  746. };
  747. primitiveOptions._updateAndQueueCommandsFunction = function (
  748. primitive,
  749. frameState,
  750. colorCommands,
  751. pickCommands,
  752. modelMatrix,
  753. cull,
  754. debugShowBoundingVolume,
  755. twoPasses
  756. ) {
  757. updateAndQueueCommands(
  758. that,
  759. frameState,
  760. colorCommands,
  761. pickCommands,
  762. modelMatrix,
  763. cull,
  764. debugShowBoundingVolume
  765. );
  766. };
  767. this._primitive = new Primitive(primitiveOptions);
  768. }
  769. if (
  770. this.appearance instanceof PolylineColorAppearance &&
  771. !this._hasPerInstanceColors
  772. ) {
  773. throw new DeveloperError(
  774. "All GeometryInstances must have color attributes to use PolylineColorAppearance with GroundPolylinePrimitive."
  775. );
  776. }
  777. this._primitive.appearance = this.appearance;
  778. this._primitive.show = this.show;
  779. this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
  780. this._primitive.update(frameState);
  781. frameState.afterRender.push(() => {
  782. if (!this._ready && defined(this._primitive) && this._primitive.ready) {
  783. this._completeLoad();
  784. }
  785. });
  786. };
  787. /**
  788. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  789. *
  790. * @param {*} id The id of the {@link GeometryInstance}.
  791. * @returns {object} The typed array in the attribute's format or undefined if the is no instance with id.
  792. *
  793. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  794. *
  795. * @example
  796. * const attributes = primitive.getGeometryInstanceAttributes('an id');
  797. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  798. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  799. */
  800. GroundPolylinePrimitive.prototype.getGeometryInstanceAttributes = function (
  801. id
  802. ) {
  803. //>>includeStart('debug', pragmas.debug);
  804. if (!defined(this._primitive)) {
  805. throw new DeveloperError(
  806. "must call update before calling getGeometryInstanceAttributes"
  807. );
  808. }
  809. //>>includeEnd('debug');
  810. return this._primitive.getGeometryInstanceAttributes(id);
  811. };
  812. /**
  813. * Checks if the given Scene supports GroundPolylinePrimitives.
  814. * GroundPolylinePrimitives require support for the WEBGL_depth_texture extension.
  815. *
  816. * @param {Scene} scene The current scene.
  817. * @returns {boolean} Whether or not the current scene supports GroundPolylinePrimitives.
  818. */
  819. GroundPolylinePrimitive.isSupported = function (scene) {
  820. return scene.frameState.context.depthTexture;
  821. };
  822. /**
  823. * Returns true if this object was destroyed; otherwise, false.
  824. * <p>
  825. * If this object was destroyed, it should not be used; calling any function other than
  826. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  827. * </p>
  828. *
  829. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  830. *
  831. * @see GroundPolylinePrimitive#destroy
  832. */
  833. GroundPolylinePrimitive.prototype.isDestroyed = function () {
  834. return false;
  835. };
  836. /**
  837. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  838. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  839. * <p>
  840. * Once an object is destroyed, it should not be used; calling any function other than
  841. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  842. * assign the return value (<code>undefined</code>) to the object as done in the example.
  843. * </p>
  844. *
  845. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  846. *
  847. * @example
  848. * e = e && e.destroy();
  849. *
  850. * @see GroundPolylinePrimitive#isDestroyed
  851. */
  852. GroundPolylinePrimitive.prototype.destroy = function () {
  853. this._primitive = this._primitive && this._primitive.destroy();
  854. this._sp = this._sp && this._sp.destroy();
  855. // Derived programs, destroyed above if they existed.
  856. this._sp2D = undefined;
  857. this._spMorph = undefined;
  858. return destroyObject(this);
  859. };
  860. export default GroundPolylinePrimitive;