ClassificationPrimitive.js 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421
  1. import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
  2. import combine from "../Core/combine.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 DrawCommand from "../Renderer/DrawCommand.js";
  10. import Pass from "../Renderer/Pass.js";
  11. import RenderState from "../Renderer/RenderState.js";
  12. import ShaderProgram from "../Renderer/ShaderProgram.js";
  13. import ShaderSource from "../Renderer/ShaderSource.js";
  14. import ShadowVolumeAppearanceVS from "../Shaders/ShadowVolumeAppearanceVS.js";
  15. import ShadowVolumeFS from "../Shaders/ShadowVolumeFS.js";
  16. import BlendingState from "./BlendingState.js";
  17. import ClassificationType from "./ClassificationType.js";
  18. import DepthFunction from "./DepthFunction.js";
  19. import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";
  20. import Primitive from "./Primitive.js";
  21. import SceneMode from "./SceneMode.js";
  22. import ShadowVolumeAppearance from "./ShadowVolumeAppearance.js";
  23. import StencilConstants from "./StencilConstants.js";
  24. import StencilFunction from "./StencilFunction.js";
  25. import StencilOperation from "./StencilOperation.js";
  26. /**
  27. * A classification primitive represents a volume enclosing geometry in the {@link Scene} to be highlighted.
  28. * <p>
  29. * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including
  30. * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement,
  31. * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix
  32. * and match most of them and add a new geometry or appearance independently of each other.
  33. * Only {@link PerInstanceColorAppearance} with the same color across all instances is supported at this time when using
  34. * ClassificationPrimitive directly.
  35. * For full {@link Appearance} support when classifying terrain or 3D Tiles use {@link GroundPrimitive} instead.
  36. * </p>
  37. * <p>
  38. * For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there
  39. * will be rendering artifacts for some viewing angles.
  40. * </p>
  41. * <p>
  42. * Valid geometries are {@link BoxGeometry}, {@link CylinderGeometry}, {@link EllipsoidGeometry}, {@link PolylineVolumeGeometry}, and {@link SphereGeometry}.
  43. * </p>
  44. * <p>
  45. * Geometries that follow the surface of the ellipsoid, such as {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry},
  46. * are also valid if they are extruded volumes; otherwise, they will not be rendered.
  47. * </p>
  48. *
  49. * @alias ClassificationPrimitive
  50. * @constructor
  51. *
  52. * @param {Object} [options] Object with the following properties:
  53. * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. This can either be a single instance or an array of length one.
  54. * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to PerInstanceColorAppearance when GeometryInstances have a color attribute.
  55. * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
  56. * @param {Boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  57. * @param {Boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
  58. * @param {Boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory.
  59. * @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.
  60. * @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.
  61. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first.
  62. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified.
  63. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  64. * @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
  65. * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be <code>false</code>.
  66. *
  67. * @see Primitive
  68. * @see GroundPrimitive
  69. * @see GeometryInstance
  70. * @see Appearance
  71. */
  72. function ClassificationPrimitive(options) {
  73. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  74. const geometryInstances = options.geometryInstances;
  75. /**
  76. * The geometry instance rendered with this primitive. This may
  77. * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>
  78. * is <code>true</code> when the primitive is constructed.
  79. * <p>
  80. * Changing this property after the primitive is rendered has no effect.
  81. * </p>
  82. * <p>
  83. * Because of the rendering technique used, all geometry instances must be the same color.
  84. * If there is an instance with a differing color, a <code>DeveloperError</code> will be thrown
  85. * on the first attempt to render.
  86. * </p>
  87. *
  88. * @readonly
  89. * @type {Array|GeometryInstance}
  90. *
  91. * @default undefined
  92. */
  93. this.geometryInstances = geometryInstances;
  94. /**
  95. * Determines if the primitive will be shown. This affects all geometry
  96. * instances in the primitive.
  97. *
  98. * @type {Boolean}
  99. *
  100. * @default true
  101. */
  102. this.show = defaultValue(options.show, true);
  103. /**
  104. * Determines whether terrain, 3D Tiles or both will be classified.
  105. *
  106. * @type {ClassificationType}
  107. *
  108. * @default ClassificationType.BOTH
  109. */
  110. this.classificationType = defaultValue(
  111. options.classificationType,
  112. ClassificationType.BOTH
  113. );
  114. /**
  115. * This property is for debugging only; it is not for production use nor is it optimized.
  116. * <p>
  117. * Draws the bounding sphere for each draw command in the primitive.
  118. * </p>
  119. *
  120. * @type {Boolean}
  121. *
  122. * @default false
  123. */
  124. this.debugShowBoundingVolume = defaultValue(
  125. options.debugShowBoundingVolume,
  126. false
  127. );
  128. /**
  129. * This property is for debugging only; it is not for production use nor is it optimized.
  130. * <p>
  131. * Draws the shadow volume for each geometry in the primitive.
  132. * </p>
  133. *
  134. * @type {Boolean}
  135. *
  136. * @default false
  137. */
  138. this.debugShowShadowVolume = defaultValue(
  139. options.debugShowShadowVolume,
  140. false
  141. );
  142. this._debugShowShadowVolume = false;
  143. // These are used by GroundPrimitive to augment the shader and uniform map.
  144. this._extruded = defaultValue(options._extruded, false);
  145. this._uniformMap = options._uniformMap;
  146. this._sp = undefined;
  147. this._spStencil = undefined;
  148. this._spPick = undefined;
  149. this._spColor = undefined;
  150. this._spPick2D = undefined; // only derived if necessary
  151. this._spColor2D = undefined; // only derived if necessary
  152. this._rsStencilDepthPass = undefined;
  153. this._rsStencilDepthPass3DTiles = undefined;
  154. this._rsColorPass = undefined;
  155. this._rsPickPass = undefined;
  156. this._commandsIgnoreShow = [];
  157. this._ready = false;
  158. this._readyPromise = defer();
  159. this._primitive = undefined;
  160. this._pickPrimitive = options._pickPrimitive;
  161. // Set in update
  162. this._hasSphericalExtentsAttribute = false;
  163. this._hasPlanarExtentsAttributes = false;
  164. this._hasPerColorAttribute = false;
  165. this.appearance = options.appearance;
  166. this._createBoundingVolumeFunction = options._createBoundingVolumeFunction;
  167. this._updateAndQueueCommandsFunction =
  168. options._updateAndQueueCommandsFunction;
  169. this._usePickOffsets = false;
  170. this._primitiveOptions = {
  171. geometryInstances: undefined,
  172. appearance: undefined,
  173. vertexCacheOptimize: defaultValue(options.vertexCacheOptimize, false),
  174. interleave: defaultValue(options.interleave, false),
  175. releaseGeometryInstances: defaultValue(
  176. options.releaseGeometryInstances,
  177. true
  178. ),
  179. allowPicking: defaultValue(options.allowPicking, true),
  180. asynchronous: defaultValue(options.asynchronous, true),
  181. compressVertices: defaultValue(options.compressVertices, true),
  182. _createBoundingVolumeFunction: undefined,
  183. _createRenderStatesFunction: undefined,
  184. _createShaderProgramFunction: undefined,
  185. _createCommandsFunction: undefined,
  186. _updateAndQueueCommandsFunction: undefined,
  187. _createPickOffsets: true,
  188. };
  189. }
  190. Object.defineProperties(ClassificationPrimitive.prototype, {
  191. /**
  192. * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  193. *
  194. * @memberof ClassificationPrimitive.prototype
  195. *
  196. * @type {Boolean}
  197. * @readonly
  198. *
  199. * @default true
  200. */
  201. vertexCacheOptimize: {
  202. get: function () {
  203. return this._primitiveOptions.vertexCacheOptimize;
  204. },
  205. },
  206. /**
  207. * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
  208. *
  209. * @memberof ClassificationPrimitive.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 ClassificationPrimitive.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 ClassificationPrimitive.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 ClassificationPrimitive.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. * When <code>true</code>, geometry vertices are compressed, which will save memory.
  268. *
  269. * @memberof ClassificationPrimitive.prototype
  270. *
  271. * @type {Boolean}
  272. * @readonly
  273. *
  274. * @default true
  275. */
  276. compressVertices: {
  277. get: function () {
  278. return this._primitiveOptions.compressVertices;
  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 ClassificationPrimitive#update}
  284. * is called.
  285. *
  286. * @memberof ClassificationPrimitive.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 ClassificationPrimitive.prototype
  299. * @type {Promise.<ClassificationPrimitive>}
  300. * @readonly
  301. */
  302. readyPromise: {
  303. get: function () {
  304. return this._readyPromise.promise;
  305. },
  306. },
  307. /**
  308. * Returns true if the ClassificationPrimitive needs a separate shader and commands for 2D.
  309. * This is because texture coordinates on ClassificationPrimitives are computed differently,
  310. * and are used for culling when multiple GeometryInstances are batched in one ClassificationPrimitive.
  311. * @memberof ClassificationPrimitive.prototype
  312. * @type {Boolean}
  313. * @readonly
  314. * @private
  315. */
  316. _needs2DShader: {
  317. get: function () {
  318. return (
  319. this._hasPlanarExtentsAttributes || this._hasSphericalExtentsAttribute
  320. );
  321. },
  322. },
  323. });
  324. /**
  325. * Determines if ClassificationPrimitive rendering is supported.
  326. *
  327. * @param {Scene} scene The scene.
  328. * @returns {Boolean} <code>true</code> if ClassificationPrimitives are supported; otherwise, returns <code>false</code>
  329. */
  330. ClassificationPrimitive.isSupported = function (scene) {
  331. return scene.context.stencilBuffer;
  332. };
  333. function getStencilDepthRenderState(enableStencil, mask3DTiles) {
  334. const stencilFunction = mask3DTiles
  335. ? StencilFunction.EQUAL
  336. : StencilFunction.ALWAYS;
  337. return {
  338. colorMask: {
  339. red: false,
  340. green: false,
  341. blue: false,
  342. alpha: false,
  343. },
  344. stencilTest: {
  345. enabled: enableStencil,
  346. frontFunction: stencilFunction,
  347. frontOperation: {
  348. fail: StencilOperation.KEEP,
  349. zFail: StencilOperation.DECREMENT_WRAP,
  350. zPass: StencilOperation.KEEP,
  351. },
  352. backFunction: stencilFunction,
  353. backOperation: {
  354. fail: StencilOperation.KEEP,
  355. zFail: StencilOperation.INCREMENT_WRAP,
  356. zPass: StencilOperation.KEEP,
  357. },
  358. reference: StencilConstants.CESIUM_3D_TILE_MASK,
  359. mask: StencilConstants.CESIUM_3D_TILE_MASK,
  360. },
  361. stencilMask: StencilConstants.CLASSIFICATION_MASK,
  362. depthTest: {
  363. enabled: true,
  364. func: DepthFunction.LESS_OR_EQUAL,
  365. },
  366. depthMask: false,
  367. };
  368. }
  369. function getColorRenderState(enableStencil) {
  370. return {
  371. stencilTest: {
  372. enabled: enableStencil,
  373. frontFunction: StencilFunction.NOT_EQUAL,
  374. frontOperation: {
  375. fail: StencilOperation.ZERO,
  376. zFail: StencilOperation.ZERO,
  377. zPass: StencilOperation.ZERO,
  378. },
  379. backFunction: StencilFunction.NOT_EQUAL,
  380. backOperation: {
  381. fail: StencilOperation.ZERO,
  382. zFail: StencilOperation.ZERO,
  383. zPass: StencilOperation.ZERO,
  384. },
  385. reference: 0,
  386. mask: StencilConstants.CLASSIFICATION_MASK,
  387. },
  388. stencilMask: StencilConstants.CLASSIFICATION_MASK,
  389. depthTest: {
  390. enabled: false,
  391. },
  392. depthMask: false,
  393. blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  394. };
  395. }
  396. const pickRenderState = {
  397. stencilTest: {
  398. enabled: true,
  399. frontFunction: StencilFunction.NOT_EQUAL,
  400. frontOperation: {
  401. fail: StencilOperation.ZERO,
  402. zFail: StencilOperation.ZERO,
  403. zPass: StencilOperation.ZERO,
  404. },
  405. backFunction: StencilFunction.NOT_EQUAL,
  406. backOperation: {
  407. fail: StencilOperation.ZERO,
  408. zFail: StencilOperation.ZERO,
  409. zPass: StencilOperation.ZERO,
  410. },
  411. reference: 0,
  412. mask: StencilConstants.CLASSIFICATION_MASK,
  413. },
  414. stencilMask: StencilConstants.CLASSIFICATION_MASK,
  415. depthTest: {
  416. enabled: false,
  417. },
  418. depthMask: false,
  419. };
  420. function createRenderStates(
  421. classificationPrimitive,
  422. context,
  423. appearance,
  424. twoPasses
  425. ) {
  426. if (defined(classificationPrimitive._rsStencilDepthPass)) {
  427. return;
  428. }
  429. const stencilEnabled = !classificationPrimitive.debugShowShadowVolume;
  430. classificationPrimitive._rsStencilDepthPass = RenderState.fromCache(
  431. getStencilDepthRenderState(stencilEnabled, false)
  432. );
  433. classificationPrimitive._rsStencilDepthPass3DTiles = RenderState.fromCache(
  434. getStencilDepthRenderState(stencilEnabled, true)
  435. );
  436. classificationPrimitive._rsColorPass = RenderState.fromCache(
  437. getColorRenderState(stencilEnabled, false)
  438. );
  439. classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState);
  440. }
  441. function modifyForEncodedNormals(primitive, vertexShaderSource) {
  442. if (!primitive.compressVertices) {
  443. return vertexShaderSource;
  444. }
  445. if (
  446. vertexShaderSource.search(/attribute\s+vec3\s+extrudeDirection;/g) !== -1
  447. ) {
  448. const attributeName = "compressedAttributes";
  449. //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes
  450. const attributeDecl = `attribute vec2 ${attributeName};`;
  451. const globalDecl = "vec3 extrudeDirection;\n";
  452. const decode = ` extrudeDirection = czm_octDecode(${attributeName}, 65535.0);\n`;
  453. let modifiedVS = vertexShaderSource;
  454. modifiedVS = modifiedVS.replace(
  455. /attribute\s+vec3\s+extrudeDirection;/g,
  456. ""
  457. );
  458. modifiedVS = ShaderSource.replaceMain(
  459. modifiedVS,
  460. "czm_non_compressed_main"
  461. );
  462. const compressedMain =
  463. `${"void main() \n" + "{ \n"}${decode} czm_non_compressed_main(); \n` +
  464. `}`;
  465. return [attributeDecl, globalDecl, modifiedVS, compressedMain].join("\n");
  466. }
  467. }
  468. function createShaderProgram(classificationPrimitive, frameState) {
  469. const context = frameState.context;
  470. const primitive = classificationPrimitive._primitive;
  471. let vs = ShadowVolumeAppearanceVS;
  472. vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()(
  473. vs
  474. );
  475. vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);
  476. vs = Primitive._modifyShaderPosition(
  477. classificationPrimitive,
  478. vs,
  479. frameState.scene3DOnly
  480. );
  481. vs = Primitive._updateColorAttribute(primitive, vs);
  482. const planarExtents = classificationPrimitive._hasPlanarExtentsAttributes;
  483. const cullFragmentsUsingExtents =
  484. planarExtents || classificationPrimitive._hasSphericalExtentsAttribute;
  485. if (classificationPrimitive._extruded) {
  486. vs = modifyForEncodedNormals(primitive, vs);
  487. }
  488. const extrudedDefine = classificationPrimitive._extruded
  489. ? "EXTRUDED_GEOMETRY"
  490. : "";
  491. let vsSource = new ShaderSource({
  492. defines: [extrudedDefine],
  493. sources: [vs],
  494. });
  495. const fsSource = new ShaderSource({
  496. sources: [ShadowVolumeFS],
  497. });
  498. const attributeLocations =
  499. classificationPrimitive._primitive._attributeLocations;
  500. const shadowVolumeAppearance = new ShadowVolumeAppearance(
  501. cullFragmentsUsingExtents,
  502. planarExtents,
  503. classificationPrimitive.appearance
  504. );
  505. classificationPrimitive._spStencil = ShaderProgram.replaceCache({
  506. context: context,
  507. shaderProgram: classificationPrimitive._spStencil,
  508. vertexShaderSource: vsSource,
  509. fragmentShaderSource: fsSource,
  510. attributeLocations: attributeLocations,
  511. });
  512. if (classificationPrimitive._primitive.allowPicking) {
  513. let vsPick = ShaderSource.createPickVertexShaderSource(vs);
  514. vsPick = Primitive._appendShowToShader(primitive, vsPick);
  515. vsPick = Primitive._updatePickColorAttribute(vsPick);
  516. const pickFS3D = shadowVolumeAppearance.createPickFragmentShader(false);
  517. const pickVS3D = shadowVolumeAppearance.createPickVertexShader(
  518. [extrudedDefine],
  519. vsPick,
  520. false,
  521. frameState.mapProjection
  522. );
  523. classificationPrimitive._spPick = ShaderProgram.replaceCache({
  524. context: context,
  525. shaderProgram: classificationPrimitive._spPick,
  526. vertexShaderSource: pickVS3D,
  527. fragmentShaderSource: pickFS3D,
  528. attributeLocations: attributeLocations,
  529. });
  530. // Derive a 2D pick shader if the primitive uses texture coordinate-based fragment culling,
  531. // since texture coordinates are computed differently in 2D.
  532. if (cullFragmentsUsingExtents) {
  533. let pickProgram2D = context.shaderCache.getDerivedShaderProgram(
  534. classificationPrimitive._spPick,
  535. "2dPick"
  536. );
  537. if (!defined(pickProgram2D)) {
  538. const pickFS2D = shadowVolumeAppearance.createPickFragmentShader(true);
  539. const pickVS2D = shadowVolumeAppearance.createPickVertexShader(
  540. [extrudedDefine],
  541. vsPick,
  542. true,
  543. frameState.mapProjection
  544. );
  545. pickProgram2D = context.shaderCache.createDerivedShaderProgram(
  546. classificationPrimitive._spPick,
  547. "2dPick",
  548. {
  549. vertexShaderSource: pickVS2D,
  550. fragmentShaderSource: pickFS2D,
  551. attributeLocations: attributeLocations,
  552. }
  553. );
  554. }
  555. classificationPrimitive._spPick2D = pickProgram2D;
  556. }
  557. } else {
  558. classificationPrimitive._spPick = ShaderProgram.fromCache({
  559. context: context,
  560. vertexShaderSource: vsSource,
  561. fragmentShaderSource: fsSource,
  562. attributeLocations: attributeLocations,
  563. });
  564. }
  565. vs = Primitive._appendShowToShader(primitive, vs);
  566. vsSource = new ShaderSource({
  567. defines: [extrudedDefine],
  568. sources: [vs],
  569. });
  570. classificationPrimitive._sp = ShaderProgram.replaceCache({
  571. context: context,
  572. shaderProgram: classificationPrimitive._sp,
  573. vertexShaderSource: vsSource,
  574. fragmentShaderSource: fsSource,
  575. attributeLocations: attributeLocations,
  576. });
  577. // Create a fragment shader that computes only required material hookups using screen space techniques
  578. const fsColorSource = shadowVolumeAppearance.createFragmentShader(false);
  579. const vsColorSource = shadowVolumeAppearance.createVertexShader(
  580. [extrudedDefine],
  581. vs,
  582. false,
  583. frameState.mapProjection
  584. );
  585. classificationPrimitive._spColor = ShaderProgram.replaceCache({
  586. context: context,
  587. shaderProgram: classificationPrimitive._spColor,
  588. vertexShaderSource: vsColorSource,
  589. fragmentShaderSource: fsColorSource,
  590. attributeLocations: attributeLocations,
  591. });
  592. // Derive a 2D shader if the primitive uses texture coordinate-based fragment culling,
  593. // since texture coordinates are computed differently in 2D.
  594. // Any material that uses texture coordinates will also equip texture coordinate-based fragment culling.
  595. if (cullFragmentsUsingExtents) {
  596. let colorProgram2D = context.shaderCache.getDerivedShaderProgram(
  597. classificationPrimitive._spColor,
  598. "2dColor"
  599. );
  600. if (!defined(colorProgram2D)) {
  601. const fsColorSource2D = shadowVolumeAppearance.createFragmentShader(true);
  602. const vsColorSource2D = shadowVolumeAppearance.createVertexShader(
  603. [extrudedDefine],
  604. vs,
  605. true,
  606. frameState.mapProjection
  607. );
  608. colorProgram2D = context.shaderCache.createDerivedShaderProgram(
  609. classificationPrimitive._spColor,
  610. "2dColor",
  611. {
  612. vertexShaderSource: vsColorSource2D,
  613. fragmentShaderSource: fsColorSource2D,
  614. attributeLocations: attributeLocations,
  615. }
  616. );
  617. }
  618. classificationPrimitive._spColor2D = colorProgram2D;
  619. }
  620. }
  621. function createColorCommands(classificationPrimitive, colorCommands) {
  622. const primitive = classificationPrimitive._primitive;
  623. let length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill
  624. colorCommands.length = length;
  625. let i;
  626. let command;
  627. let derivedCommand;
  628. let vaIndex = 0;
  629. let uniformMap = primitive._batchTable.getUniformMapCallback()(
  630. classificationPrimitive._uniformMap
  631. );
  632. const needs2DShader = classificationPrimitive._needs2DShader;
  633. for (i = 0; i < length; i += 2) {
  634. const vertexArray = primitive._va[vaIndex++];
  635. // Stencil depth command
  636. command = colorCommands[i];
  637. if (!defined(command)) {
  638. command = colorCommands[i] = new DrawCommand({
  639. owner: classificationPrimitive,
  640. primitiveType: primitive._primitiveType,
  641. });
  642. }
  643. command.vertexArray = vertexArray;
  644. command.renderState = classificationPrimitive._rsStencilDepthPass;
  645. command.shaderProgram = classificationPrimitive._sp;
  646. command.uniformMap = uniformMap;
  647. command.pass = Pass.TERRAIN_CLASSIFICATION;
  648. derivedCommand = DrawCommand.shallowClone(
  649. command,
  650. command.derivedCommands.tileset
  651. );
  652. derivedCommand.renderState =
  653. classificationPrimitive._rsStencilDepthPass3DTiles;
  654. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  655. command.derivedCommands.tileset = derivedCommand;
  656. // Color command
  657. command = colorCommands[i + 1];
  658. if (!defined(command)) {
  659. command = colorCommands[i + 1] = new DrawCommand({
  660. owner: classificationPrimitive,
  661. primitiveType: primitive._primitiveType,
  662. });
  663. }
  664. command.vertexArray = vertexArray;
  665. command.renderState = classificationPrimitive._rsColorPass;
  666. command.shaderProgram = classificationPrimitive._spColor;
  667. command.pass = Pass.TERRAIN_CLASSIFICATION;
  668. const appearance = classificationPrimitive.appearance;
  669. const material = appearance.material;
  670. if (defined(material)) {
  671. uniformMap = combine(uniformMap, material._uniforms);
  672. }
  673. command.uniformMap = uniformMap;
  674. derivedCommand = DrawCommand.shallowClone(
  675. command,
  676. command.derivedCommands.tileset
  677. );
  678. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  679. command.derivedCommands.tileset = derivedCommand;
  680. // Derive for 2D if texture coordinates are ever computed
  681. if (needs2DShader) {
  682. // First derive from the terrain command
  683. let derived2DCommand = DrawCommand.shallowClone(
  684. command,
  685. command.derivedCommands.appearance2D
  686. );
  687. derived2DCommand.shaderProgram = classificationPrimitive._spColor2D;
  688. command.derivedCommands.appearance2D = derived2DCommand;
  689. // Then derive from the 3D Tiles command
  690. derived2DCommand = DrawCommand.shallowClone(
  691. derivedCommand,
  692. derivedCommand.derivedCommands.appearance2D
  693. );
  694. derived2DCommand.shaderProgram = classificationPrimitive._spColor2D;
  695. derivedCommand.derivedCommands.appearance2D = derived2DCommand;
  696. }
  697. }
  698. const commandsIgnoreShow = classificationPrimitive._commandsIgnoreShow;
  699. const spStencil = classificationPrimitive._spStencil;
  700. let commandIndex = 0;
  701. length = commandsIgnoreShow.length = length / 2;
  702. for (let j = 0; j < length; ++j) {
  703. const commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone(
  704. colorCommands[commandIndex],
  705. commandsIgnoreShow[j]
  706. ));
  707. commandIgnoreShow.shaderProgram = spStencil;
  708. commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;
  709. commandIndex += 2;
  710. }
  711. }
  712. function createPickCommands(classificationPrimitive, pickCommands) {
  713. const usePickOffsets = classificationPrimitive._usePickOffsets;
  714. const primitive = classificationPrimitive._primitive;
  715. let length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill
  716. // Fallback for batching same-color geometry instances
  717. let pickOffsets;
  718. let pickIndex = 0;
  719. let pickOffset;
  720. if (usePickOffsets) {
  721. pickOffsets = primitive._pickOffsets;
  722. length = pickOffsets.length * 2;
  723. }
  724. pickCommands.length = length;
  725. let j;
  726. let command;
  727. let derivedCommand;
  728. let vaIndex = 0;
  729. const uniformMap = primitive._batchTable.getUniformMapCallback()(
  730. classificationPrimitive._uniformMap
  731. );
  732. const needs2DShader = classificationPrimitive._needs2DShader;
  733. for (j = 0; j < length; j += 2) {
  734. let vertexArray = primitive._va[vaIndex++];
  735. if (usePickOffsets) {
  736. pickOffset = pickOffsets[pickIndex++];
  737. vertexArray = primitive._va[pickOffset.index];
  738. }
  739. // Stencil depth command
  740. command = pickCommands[j];
  741. if (!defined(command)) {
  742. command = pickCommands[j] = new DrawCommand({
  743. owner: classificationPrimitive,
  744. primitiveType: primitive._primitiveType,
  745. pickOnly: true,
  746. });
  747. }
  748. command.vertexArray = vertexArray;
  749. command.renderState = classificationPrimitive._rsStencilDepthPass;
  750. command.shaderProgram = classificationPrimitive._sp;
  751. command.uniformMap = uniformMap;
  752. command.pass = Pass.TERRAIN_CLASSIFICATION;
  753. if (usePickOffsets) {
  754. command.offset = pickOffset.offset;
  755. command.count = pickOffset.count;
  756. }
  757. // Derive for 3D Tiles classification
  758. derivedCommand = DrawCommand.shallowClone(
  759. command,
  760. command.derivedCommands.tileset
  761. );
  762. derivedCommand.renderState =
  763. classificationPrimitive._rsStencilDepthPass3DTiles;
  764. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  765. command.derivedCommands.tileset = derivedCommand;
  766. // Pick color command
  767. command = pickCommands[j + 1];
  768. if (!defined(command)) {
  769. command = pickCommands[j + 1] = new DrawCommand({
  770. owner: classificationPrimitive,
  771. primitiveType: primitive._primitiveType,
  772. pickOnly: true,
  773. });
  774. }
  775. command.vertexArray = vertexArray;
  776. command.renderState = classificationPrimitive._rsPickPass;
  777. command.shaderProgram = classificationPrimitive._spPick;
  778. command.uniformMap = uniformMap;
  779. command.pass = Pass.TERRAIN_CLASSIFICATION;
  780. if (usePickOffsets) {
  781. command.offset = pickOffset.offset;
  782. command.count = pickOffset.count;
  783. }
  784. derivedCommand = DrawCommand.shallowClone(
  785. command,
  786. command.derivedCommands.tileset
  787. );
  788. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  789. command.derivedCommands.tileset = derivedCommand;
  790. // Derive for 2D if texture coordinates are ever computed
  791. if (needs2DShader) {
  792. // First derive from the terrain command
  793. let derived2DCommand = DrawCommand.shallowClone(
  794. command,
  795. command.derivedCommands.pick2D
  796. );
  797. derived2DCommand.shaderProgram = classificationPrimitive._spPick2D;
  798. command.derivedCommands.pick2D = derived2DCommand;
  799. // Then derive from the 3D Tiles command
  800. derived2DCommand = DrawCommand.shallowClone(
  801. derivedCommand,
  802. derivedCommand.derivedCommands.pick2D
  803. );
  804. derived2DCommand.shaderProgram = classificationPrimitive._spPick2D;
  805. derivedCommand.derivedCommands.pick2D = derived2DCommand;
  806. }
  807. }
  808. }
  809. function createCommands(
  810. classificationPrimitive,
  811. appearance,
  812. material,
  813. translucent,
  814. twoPasses,
  815. colorCommands,
  816. pickCommands
  817. ) {
  818. createColorCommands(classificationPrimitive, colorCommands);
  819. createPickCommands(classificationPrimitive, pickCommands);
  820. }
  821. function boundingVolumeIndex(commandIndex, length) {
  822. return Math.floor((commandIndex % length) / 2);
  823. }
  824. function updateAndQueueRenderCommand(
  825. command,
  826. frameState,
  827. modelMatrix,
  828. cull,
  829. boundingVolume,
  830. debugShowBoundingVolume
  831. ) {
  832. command.modelMatrix = modelMatrix;
  833. command.boundingVolume = boundingVolume;
  834. command.cull = cull;
  835. command.debugShowBoundingVolume = debugShowBoundingVolume;
  836. frameState.commandList.push(command);
  837. }
  838. function updateAndQueuePickCommand(
  839. command,
  840. frameState,
  841. modelMatrix,
  842. cull,
  843. boundingVolume
  844. ) {
  845. command.modelMatrix = modelMatrix;
  846. command.boundingVolume = boundingVolume;
  847. command.cull = cull;
  848. frameState.commandList.push(command);
  849. }
  850. function updateAndQueueCommands(
  851. classificationPrimitive,
  852. frameState,
  853. colorCommands,
  854. pickCommands,
  855. modelMatrix,
  856. cull,
  857. debugShowBoundingVolume,
  858. twoPasses
  859. ) {
  860. const primitive = classificationPrimitive._primitive;
  861. Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix);
  862. let boundingVolumes;
  863. if (frameState.mode === SceneMode.SCENE3D) {
  864. boundingVolumes = primitive._boundingSphereWC;
  865. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  866. boundingVolumes = primitive._boundingSphereCV;
  867. } else if (
  868. frameState.mode === SceneMode.SCENE2D &&
  869. defined(primitive._boundingSphere2D)
  870. ) {
  871. boundingVolumes = primitive._boundingSphere2D;
  872. } else if (defined(primitive._boundingSphereMorph)) {
  873. boundingVolumes = primitive._boundingSphereMorph;
  874. }
  875. const classificationType = classificationPrimitive.classificationType;
  876. const queueTerrainCommands =
  877. classificationType !== ClassificationType.CESIUM_3D_TILE;
  878. const queue3DTilesCommands =
  879. classificationType !== ClassificationType.TERRAIN;
  880. const passes = frameState.passes;
  881. let i;
  882. let boundingVolume;
  883. let command;
  884. if (passes.render) {
  885. const colorLength = colorCommands.length;
  886. for (i = 0; i < colorLength; ++i) {
  887. boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)];
  888. if (queueTerrainCommands) {
  889. command = colorCommands[i];
  890. updateAndQueueRenderCommand(
  891. command,
  892. frameState,
  893. modelMatrix,
  894. cull,
  895. boundingVolume,
  896. debugShowBoundingVolume
  897. );
  898. }
  899. if (queue3DTilesCommands) {
  900. command = colorCommands[i].derivedCommands.tileset;
  901. updateAndQueueRenderCommand(
  902. command,
  903. frameState,
  904. modelMatrix,
  905. cull,
  906. boundingVolume,
  907. debugShowBoundingVolume
  908. );
  909. }
  910. }
  911. if (frameState.invertClassification) {
  912. const ignoreShowCommands = classificationPrimitive._commandsIgnoreShow;
  913. const ignoreShowCommandsLength = ignoreShowCommands.length;
  914. for (i = 0; i < ignoreShowCommandsLength; ++i) {
  915. boundingVolume = boundingVolumes[i];
  916. command = ignoreShowCommands[i];
  917. updateAndQueueRenderCommand(
  918. command,
  919. frameState,
  920. modelMatrix,
  921. cull,
  922. boundingVolume,
  923. debugShowBoundingVolume
  924. );
  925. }
  926. }
  927. }
  928. if (passes.pick) {
  929. const pickLength = pickCommands.length;
  930. const pickOffsets = primitive._pickOffsets;
  931. for (i = 0; i < pickLength; ++i) {
  932. const pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)];
  933. boundingVolume = boundingVolumes[pickOffset.index];
  934. if (queueTerrainCommands) {
  935. command = pickCommands[i];
  936. updateAndQueuePickCommand(
  937. command,
  938. frameState,
  939. modelMatrix,
  940. cull,
  941. boundingVolume
  942. );
  943. }
  944. if (queue3DTilesCommands) {
  945. command = pickCommands[i].derivedCommands.tileset;
  946. updateAndQueuePickCommand(
  947. command,
  948. frameState,
  949. modelMatrix,
  950. cull,
  951. boundingVolume
  952. );
  953. }
  954. }
  955. }
  956. }
  957. /**
  958. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  959. * get the draw commands needed to render this primitive.
  960. * <p>
  961. * Do not call this function directly. This is documented just to
  962. * list the exceptions that may be propagated when the scene is rendered:
  963. * </p>
  964. *
  965. * @exception {DeveloperError} All instance geometries must have the same primitiveType.
  966. * @exception {DeveloperError} Appearance and material have a uniform with the same name.
  967. * @exception {DeveloperError} Not all of the geometry instances have the same color attribute.
  968. */
  969. ClassificationPrimitive.prototype.update = function (frameState) {
  970. if (!defined(this._primitive) && !defined(this.geometryInstances)) {
  971. return;
  972. }
  973. let appearance = this.appearance;
  974. if (defined(appearance) && defined(appearance.material)) {
  975. appearance.material.update(frameState.context);
  976. }
  977. const that = this;
  978. const primitiveOptions = this._primitiveOptions;
  979. if (!defined(this._primitive)) {
  980. const instances = Array.isArray(this.geometryInstances)
  981. ? this.geometryInstances
  982. : [this.geometryInstances];
  983. const length = instances.length;
  984. let i;
  985. let instance;
  986. let attributes;
  987. let hasPerColorAttribute = false;
  988. let allColorsSame = true;
  989. let firstColor;
  990. let hasSphericalExtentsAttribute = false;
  991. let hasPlanarExtentsAttributes = false;
  992. if (length > 0) {
  993. attributes = instances[0].attributes;
  994. // Not expecting these to be set by users, should only be set via GroundPrimitive.
  995. // So don't check for mismatch.
  996. hasSphericalExtentsAttribute = ShadowVolumeAppearance.hasAttributesForSphericalExtents(
  997. attributes
  998. );
  999. hasPlanarExtentsAttributes = ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(
  1000. attributes
  1001. );
  1002. firstColor = attributes.color;
  1003. }
  1004. for (i = 0; i < length; i++) {
  1005. instance = instances[i];
  1006. const color = instance.attributes.color;
  1007. if (defined(color)) {
  1008. hasPerColorAttribute = true;
  1009. }
  1010. //>>includeStart('debug', pragmas.debug);
  1011. else if (hasPerColorAttribute) {
  1012. throw new DeveloperError(
  1013. "All GeometryInstances must have color attributes to use per-instance color."
  1014. );
  1015. }
  1016. //>>includeEnd('debug');
  1017. allColorsSame =
  1018. allColorsSame &&
  1019. defined(color) &&
  1020. ColorGeometryInstanceAttribute.equals(firstColor, color);
  1021. }
  1022. // If no attributes exist for computing spherical extents or fragment culling,
  1023. // throw if the colors aren't all the same.
  1024. if (
  1025. !allColorsSame &&
  1026. !hasSphericalExtentsAttribute &&
  1027. !hasPlanarExtentsAttributes
  1028. ) {
  1029. throw new DeveloperError(
  1030. "All GeometryInstances must have the same color attribute except via GroundPrimitives"
  1031. );
  1032. }
  1033. // default to a color appearance
  1034. if (hasPerColorAttribute && !defined(appearance)) {
  1035. appearance = new PerInstanceColorAppearance({
  1036. flat: true,
  1037. });
  1038. this.appearance = appearance;
  1039. }
  1040. //>>includeStart('debug', pragmas.debug);
  1041. if (
  1042. !hasPerColorAttribute &&
  1043. appearance instanceof PerInstanceColorAppearance
  1044. ) {
  1045. throw new DeveloperError(
  1046. "PerInstanceColorAppearance requires color GeometryInstanceAttributes on all GeometryInstances"
  1047. );
  1048. }
  1049. if (
  1050. defined(appearance.material) &&
  1051. !hasSphericalExtentsAttribute &&
  1052. !hasPlanarExtentsAttributes
  1053. ) {
  1054. throw new DeveloperError(
  1055. "Materials on ClassificationPrimitives are not supported except via GroundPrimitives"
  1056. );
  1057. }
  1058. //>>includeEnd('debug');
  1059. this._usePickOffsets =
  1060. !hasSphericalExtentsAttribute && !hasPlanarExtentsAttributes;
  1061. this._hasSphericalExtentsAttribute = hasSphericalExtentsAttribute;
  1062. this._hasPlanarExtentsAttributes = hasPlanarExtentsAttributes;
  1063. this._hasPerColorAttribute = hasPerColorAttribute;
  1064. const geometryInstances = new Array(length);
  1065. for (i = 0; i < length; ++i) {
  1066. instance = instances[i];
  1067. geometryInstances[i] = new GeometryInstance({
  1068. geometry: instance.geometry,
  1069. attributes: instance.attributes,
  1070. modelMatrix: instance.modelMatrix,
  1071. id: instance.id,
  1072. pickPrimitive: defaultValue(this._pickPrimitive, that),
  1073. });
  1074. }
  1075. primitiveOptions.appearance = appearance;
  1076. primitiveOptions.geometryInstances = geometryInstances;
  1077. if (defined(this._createBoundingVolumeFunction)) {
  1078. primitiveOptions._createBoundingVolumeFunction = function (
  1079. frameState,
  1080. geometry
  1081. ) {
  1082. that._createBoundingVolumeFunction(frameState, geometry);
  1083. };
  1084. }
  1085. primitiveOptions._createRenderStatesFunction = function (
  1086. primitive,
  1087. context,
  1088. appearance,
  1089. twoPasses
  1090. ) {
  1091. createRenderStates(that, context);
  1092. };
  1093. primitiveOptions._createShaderProgramFunction = function (
  1094. primitive,
  1095. frameState,
  1096. appearance
  1097. ) {
  1098. createShaderProgram(that, frameState);
  1099. };
  1100. primitiveOptions._createCommandsFunction = function (
  1101. primitive,
  1102. appearance,
  1103. material,
  1104. translucent,
  1105. twoPasses,
  1106. colorCommands,
  1107. pickCommands
  1108. ) {
  1109. createCommands(
  1110. that,
  1111. undefined,
  1112. undefined,
  1113. true,
  1114. false,
  1115. colorCommands,
  1116. pickCommands
  1117. );
  1118. };
  1119. if (defined(this._updateAndQueueCommandsFunction)) {
  1120. primitiveOptions._updateAndQueueCommandsFunction = function (
  1121. primitive,
  1122. frameState,
  1123. colorCommands,
  1124. pickCommands,
  1125. modelMatrix,
  1126. cull,
  1127. debugShowBoundingVolume,
  1128. twoPasses
  1129. ) {
  1130. that._updateAndQueueCommandsFunction(
  1131. primitive,
  1132. frameState,
  1133. colorCommands,
  1134. pickCommands,
  1135. modelMatrix,
  1136. cull,
  1137. debugShowBoundingVolume,
  1138. twoPasses
  1139. );
  1140. };
  1141. } else {
  1142. primitiveOptions._updateAndQueueCommandsFunction = function (
  1143. primitive,
  1144. frameState,
  1145. colorCommands,
  1146. pickCommands,
  1147. modelMatrix,
  1148. cull,
  1149. debugShowBoundingVolume,
  1150. twoPasses
  1151. ) {
  1152. updateAndQueueCommands(
  1153. that,
  1154. frameState,
  1155. colorCommands,
  1156. pickCommands,
  1157. modelMatrix,
  1158. cull,
  1159. debugShowBoundingVolume,
  1160. twoPasses
  1161. );
  1162. };
  1163. }
  1164. this._primitive = new Primitive(primitiveOptions);
  1165. this._primitive.readyPromise.then(function (primitive) {
  1166. that._ready = true;
  1167. if (that.releaseGeometryInstances) {
  1168. that.geometryInstances = undefined;
  1169. }
  1170. const error = primitive._error;
  1171. if (!defined(error)) {
  1172. that._readyPromise.resolve(that);
  1173. } else {
  1174. that._readyPromise.reject(error);
  1175. }
  1176. });
  1177. }
  1178. if (
  1179. this.debugShowShadowVolume &&
  1180. !this._debugShowShadowVolume &&
  1181. this._ready
  1182. ) {
  1183. this._debugShowShadowVolume = true;
  1184. this._rsStencilDepthPass = RenderState.fromCache(
  1185. getStencilDepthRenderState(false, false)
  1186. );
  1187. this._rsStencilDepthPass3DTiles = RenderState.fromCache(
  1188. getStencilDepthRenderState(false, true)
  1189. );
  1190. this._rsColorPass = RenderState.fromCache(getColorRenderState(false));
  1191. } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) {
  1192. this._debugShowShadowVolume = false;
  1193. this._rsStencilDepthPass = RenderState.fromCache(
  1194. getStencilDepthRenderState(true, false)
  1195. );
  1196. this._rsStencilDepthPass3DTiles = RenderState.fromCache(
  1197. getStencilDepthRenderState(true, true)
  1198. );
  1199. this._rsColorPass = RenderState.fromCache(getColorRenderState(true));
  1200. }
  1201. // Update primitive appearance
  1202. if (this._primitive.appearance !== appearance) {
  1203. //>>includeStart('debug', pragmas.debug);
  1204. // Check if the appearance is supported by the geometry attributes
  1205. if (
  1206. !this._hasSphericalExtentsAttribute &&
  1207. !this._hasPlanarExtentsAttributes &&
  1208. defined(appearance.material)
  1209. ) {
  1210. throw new DeveloperError(
  1211. "Materials on ClassificationPrimitives are not supported except via GroundPrimitive"
  1212. );
  1213. }
  1214. if (
  1215. !this._hasPerColorAttribute &&
  1216. appearance instanceof PerInstanceColorAppearance
  1217. ) {
  1218. throw new DeveloperError(
  1219. "PerInstanceColorAppearance requires color GeometryInstanceAttribute"
  1220. );
  1221. }
  1222. //>>includeEnd('debug');
  1223. this._primitive.appearance = appearance;
  1224. }
  1225. this._primitive.show = this.show;
  1226. this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
  1227. this._primitive.update(frameState);
  1228. };
  1229. /**
  1230. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  1231. *
  1232. * @param {*} id The id of the {@link GeometryInstance}.
  1233. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id.
  1234. *
  1235. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  1236. *
  1237. * @example
  1238. * const attributes = primitive.getGeometryInstanceAttributes('an id');
  1239. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  1240. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  1241. */
  1242. ClassificationPrimitive.prototype.getGeometryInstanceAttributes = function (
  1243. id
  1244. ) {
  1245. //>>includeStart('debug', pragmas.debug);
  1246. if (!defined(this._primitive)) {
  1247. throw new DeveloperError(
  1248. "must call update before calling getGeometryInstanceAttributes"
  1249. );
  1250. }
  1251. //>>includeEnd('debug');
  1252. return this._primitive.getGeometryInstanceAttributes(id);
  1253. };
  1254. /**
  1255. * Returns true if this object was destroyed; otherwise, false.
  1256. * <p>
  1257. * If this object was destroyed, it should not be used; calling any function other than
  1258. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1259. * </p>
  1260. *
  1261. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1262. *
  1263. * @see ClassificationPrimitive#destroy
  1264. */
  1265. ClassificationPrimitive.prototype.isDestroyed = function () {
  1266. return false;
  1267. };
  1268. /**
  1269. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1270. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1271. * <p>
  1272. * Once an object is destroyed, it should not be used; calling any function other than
  1273. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1274. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1275. * </p>
  1276. *
  1277. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1278. *
  1279. * @example
  1280. * e = e && e.destroy();
  1281. *
  1282. * @see ClassificationPrimitive#isDestroyed
  1283. */
  1284. ClassificationPrimitive.prototype.destroy = function () {
  1285. this._primitive = this._primitive && this._primitive.destroy();
  1286. this._sp = this._sp && this._sp.destroy();
  1287. this._spPick = this._spPick && this._spPick.destroy();
  1288. this._spColor = this._spColor && this._spColor.destroy();
  1289. // Derived programs, destroyed above if they existed.
  1290. this._spPick2D = undefined;
  1291. this._spColor2D = undefined;
  1292. return destroyObject(this);
  1293. };
  1294. export default ClassificationPrimitive;