ClassificationPrimitive.js 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424
  1. import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
  2. import combine from "../Core/combine.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 GeometryInstance from "../Core/GeometryInstance.js";
  8. import DrawCommand from "../Renderer/DrawCommand.js";
  9. import Pass from "../Renderer/Pass.js";
  10. import RenderState from "../Renderer/RenderState.js";
  11. import ShaderProgram from "../Renderer/ShaderProgram.js";
  12. import ShaderSource from "../Renderer/ShaderSource.js";
  13. import ShadowVolumeAppearanceVS from "../Shaders/ShadowVolumeAppearanceVS.js";
  14. import ShadowVolumeFS from "../Shaders/ShadowVolumeFS.js";
  15. import when from "../ThirdParty/when.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. var 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 = when.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. var 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. var 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. var 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. var 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. var attributeDecl = "attribute vec2 " + attributeName + ";";
  451. var globalDecl = "vec3 extrudeDirection;\n";
  452. var decode =
  453. " extrudeDirection = czm_octDecode(" + attributeName + ", 65535.0);\n";
  454. var modifiedVS = vertexShaderSource;
  455. modifiedVS = modifiedVS.replace(
  456. /attribute\s+vec3\s+extrudeDirection;/g,
  457. ""
  458. );
  459. modifiedVS = ShaderSource.replaceMain(
  460. modifiedVS,
  461. "czm_non_compressed_main"
  462. );
  463. var compressedMain =
  464. "void main() \n" +
  465. "{ \n" +
  466. decode +
  467. " czm_non_compressed_main(); \n" +
  468. "}";
  469. return [attributeDecl, globalDecl, modifiedVS, compressedMain].join("\n");
  470. }
  471. }
  472. function createShaderProgram(classificationPrimitive, frameState) {
  473. var context = frameState.context;
  474. var primitive = classificationPrimitive._primitive;
  475. var vs = ShadowVolumeAppearanceVS;
  476. vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()(
  477. vs
  478. );
  479. vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);
  480. vs = Primitive._modifyShaderPosition(
  481. classificationPrimitive,
  482. vs,
  483. frameState.scene3DOnly
  484. );
  485. vs = Primitive._updateColorAttribute(primitive, vs);
  486. var planarExtents = classificationPrimitive._hasPlanarExtentsAttributes;
  487. var cullFragmentsUsingExtents =
  488. planarExtents || classificationPrimitive._hasSphericalExtentsAttribute;
  489. if (classificationPrimitive._extruded) {
  490. vs = modifyForEncodedNormals(primitive, vs);
  491. }
  492. var extrudedDefine = classificationPrimitive._extruded
  493. ? "EXTRUDED_GEOMETRY"
  494. : "";
  495. var vsSource = new ShaderSource({
  496. defines: [extrudedDefine],
  497. sources: [vs],
  498. });
  499. var fsSource = new ShaderSource({
  500. sources: [ShadowVolumeFS],
  501. });
  502. var attributeLocations =
  503. classificationPrimitive._primitive._attributeLocations;
  504. var shadowVolumeAppearance = new ShadowVolumeAppearance(
  505. cullFragmentsUsingExtents,
  506. planarExtents,
  507. classificationPrimitive.appearance
  508. );
  509. classificationPrimitive._spStencil = ShaderProgram.replaceCache({
  510. context: context,
  511. shaderProgram: classificationPrimitive._spStencil,
  512. vertexShaderSource: vsSource,
  513. fragmentShaderSource: fsSource,
  514. attributeLocations: attributeLocations,
  515. });
  516. if (classificationPrimitive._primitive.allowPicking) {
  517. var vsPick = ShaderSource.createPickVertexShaderSource(vs);
  518. vsPick = Primitive._appendShowToShader(primitive, vsPick);
  519. vsPick = Primitive._updatePickColorAttribute(vsPick);
  520. var pickFS3D = shadowVolumeAppearance.createPickFragmentShader(false);
  521. var pickVS3D = shadowVolumeAppearance.createPickVertexShader(
  522. [extrudedDefine],
  523. vsPick,
  524. false,
  525. frameState.mapProjection
  526. );
  527. classificationPrimitive._spPick = ShaderProgram.replaceCache({
  528. context: context,
  529. shaderProgram: classificationPrimitive._spPick,
  530. vertexShaderSource: pickVS3D,
  531. fragmentShaderSource: pickFS3D,
  532. attributeLocations: attributeLocations,
  533. });
  534. // Derive a 2D pick shader if the primitive uses texture coordinate-based fragment culling,
  535. // since texture coordinates are computed differently in 2D.
  536. if (cullFragmentsUsingExtents) {
  537. var pickProgram2D = context.shaderCache.getDerivedShaderProgram(
  538. classificationPrimitive._spPick,
  539. "2dPick"
  540. );
  541. if (!defined(pickProgram2D)) {
  542. var pickFS2D = shadowVolumeAppearance.createPickFragmentShader(true);
  543. var pickVS2D = shadowVolumeAppearance.createPickVertexShader(
  544. [extrudedDefine],
  545. vsPick,
  546. true,
  547. frameState.mapProjection
  548. );
  549. pickProgram2D = context.shaderCache.createDerivedShaderProgram(
  550. classificationPrimitive._spPick,
  551. "2dPick",
  552. {
  553. vertexShaderSource: pickVS2D,
  554. fragmentShaderSource: pickFS2D,
  555. attributeLocations: attributeLocations,
  556. }
  557. );
  558. }
  559. classificationPrimitive._spPick2D = pickProgram2D;
  560. }
  561. } else {
  562. classificationPrimitive._spPick = ShaderProgram.fromCache({
  563. context: context,
  564. vertexShaderSource: vsSource,
  565. fragmentShaderSource: fsSource,
  566. attributeLocations: attributeLocations,
  567. });
  568. }
  569. vs = Primitive._appendShowToShader(primitive, vs);
  570. vsSource = new ShaderSource({
  571. defines: [extrudedDefine],
  572. sources: [vs],
  573. });
  574. classificationPrimitive._sp = ShaderProgram.replaceCache({
  575. context: context,
  576. shaderProgram: classificationPrimitive._sp,
  577. vertexShaderSource: vsSource,
  578. fragmentShaderSource: fsSource,
  579. attributeLocations: attributeLocations,
  580. });
  581. // Create a fragment shader that computes only required material hookups using screen space techniques
  582. var fsColorSource = shadowVolumeAppearance.createFragmentShader(false);
  583. var vsColorSource = shadowVolumeAppearance.createVertexShader(
  584. [extrudedDefine],
  585. vs,
  586. false,
  587. frameState.mapProjection
  588. );
  589. classificationPrimitive._spColor = ShaderProgram.replaceCache({
  590. context: context,
  591. shaderProgram: classificationPrimitive._spColor,
  592. vertexShaderSource: vsColorSource,
  593. fragmentShaderSource: fsColorSource,
  594. attributeLocations: attributeLocations,
  595. });
  596. // Derive a 2D shader if the primitive uses texture coordinate-based fragment culling,
  597. // since texture coordinates are computed differently in 2D.
  598. // Any material that uses texture coordinates will also equip texture coordinate-based fragment culling.
  599. if (cullFragmentsUsingExtents) {
  600. var colorProgram2D = context.shaderCache.getDerivedShaderProgram(
  601. classificationPrimitive._spColor,
  602. "2dColor"
  603. );
  604. if (!defined(colorProgram2D)) {
  605. var fsColorSource2D = shadowVolumeAppearance.createFragmentShader(true);
  606. var vsColorSource2D = shadowVolumeAppearance.createVertexShader(
  607. [extrudedDefine],
  608. vs,
  609. true,
  610. frameState.mapProjection
  611. );
  612. colorProgram2D = context.shaderCache.createDerivedShaderProgram(
  613. classificationPrimitive._spColor,
  614. "2dColor",
  615. {
  616. vertexShaderSource: vsColorSource2D,
  617. fragmentShaderSource: fsColorSource2D,
  618. attributeLocations: attributeLocations,
  619. }
  620. );
  621. }
  622. classificationPrimitive._spColor2D = colorProgram2D;
  623. }
  624. }
  625. function createColorCommands(classificationPrimitive, colorCommands) {
  626. var primitive = classificationPrimitive._primitive;
  627. var length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill
  628. colorCommands.length = length;
  629. var i;
  630. var command;
  631. var derivedCommand;
  632. var vaIndex = 0;
  633. var uniformMap = primitive._batchTable.getUniformMapCallback()(
  634. classificationPrimitive._uniformMap
  635. );
  636. var needs2DShader = classificationPrimitive._needs2DShader;
  637. for (i = 0; i < length; i += 2) {
  638. var vertexArray = primitive._va[vaIndex++];
  639. // Stencil depth command
  640. command = colorCommands[i];
  641. if (!defined(command)) {
  642. command = colorCommands[i] = new DrawCommand({
  643. owner: classificationPrimitive,
  644. primitiveType: primitive._primitiveType,
  645. });
  646. }
  647. command.vertexArray = vertexArray;
  648. command.renderState = classificationPrimitive._rsStencilDepthPass;
  649. command.shaderProgram = classificationPrimitive._sp;
  650. command.uniformMap = uniformMap;
  651. command.pass = Pass.TERRAIN_CLASSIFICATION;
  652. derivedCommand = DrawCommand.shallowClone(
  653. command,
  654. command.derivedCommands.tileset
  655. );
  656. derivedCommand.renderState =
  657. classificationPrimitive._rsStencilDepthPass3DTiles;
  658. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  659. command.derivedCommands.tileset = derivedCommand;
  660. // Color command
  661. command = colorCommands[i + 1];
  662. if (!defined(command)) {
  663. command = colorCommands[i + 1] = new DrawCommand({
  664. owner: classificationPrimitive,
  665. primitiveType: primitive._primitiveType,
  666. });
  667. }
  668. command.vertexArray = vertexArray;
  669. command.renderState = classificationPrimitive._rsColorPass;
  670. command.shaderProgram = classificationPrimitive._spColor;
  671. command.pass = Pass.TERRAIN_CLASSIFICATION;
  672. var appearance = classificationPrimitive.appearance;
  673. var material = appearance.material;
  674. if (defined(material)) {
  675. uniformMap = combine(uniformMap, material._uniforms);
  676. }
  677. command.uniformMap = uniformMap;
  678. derivedCommand = DrawCommand.shallowClone(
  679. command,
  680. command.derivedCommands.tileset
  681. );
  682. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  683. command.derivedCommands.tileset = derivedCommand;
  684. // Derive for 2D if texture coordinates are ever computed
  685. if (needs2DShader) {
  686. // First derive from the terrain command
  687. var derived2DCommand = DrawCommand.shallowClone(
  688. command,
  689. command.derivedCommands.appearance2D
  690. );
  691. derived2DCommand.shaderProgram = classificationPrimitive._spColor2D;
  692. command.derivedCommands.appearance2D = derived2DCommand;
  693. // Then derive from the 3D Tiles command
  694. derived2DCommand = DrawCommand.shallowClone(
  695. derivedCommand,
  696. derivedCommand.derivedCommands.appearance2D
  697. );
  698. derived2DCommand.shaderProgram = classificationPrimitive._spColor2D;
  699. derivedCommand.derivedCommands.appearance2D = derived2DCommand;
  700. }
  701. }
  702. var commandsIgnoreShow = classificationPrimitive._commandsIgnoreShow;
  703. var spStencil = classificationPrimitive._spStencil;
  704. var commandIndex = 0;
  705. length = commandsIgnoreShow.length = length / 2;
  706. for (var j = 0; j < length; ++j) {
  707. var commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone(
  708. colorCommands[commandIndex],
  709. commandsIgnoreShow[j]
  710. ));
  711. commandIgnoreShow.shaderProgram = spStencil;
  712. commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;
  713. commandIndex += 2;
  714. }
  715. }
  716. function createPickCommands(classificationPrimitive, pickCommands) {
  717. var usePickOffsets = classificationPrimitive._usePickOffsets;
  718. var primitive = classificationPrimitive._primitive;
  719. var length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill
  720. // Fallback for batching same-color geometry instances
  721. var pickOffsets;
  722. var pickIndex = 0;
  723. var pickOffset;
  724. if (usePickOffsets) {
  725. pickOffsets = primitive._pickOffsets;
  726. length = pickOffsets.length * 2;
  727. }
  728. pickCommands.length = length;
  729. var j;
  730. var command;
  731. var derivedCommand;
  732. var vaIndex = 0;
  733. var uniformMap = primitive._batchTable.getUniformMapCallback()(
  734. classificationPrimitive._uniformMap
  735. );
  736. var needs2DShader = classificationPrimitive._needs2DShader;
  737. for (j = 0; j < length; j += 2) {
  738. var vertexArray = primitive._va[vaIndex++];
  739. if (usePickOffsets) {
  740. pickOffset = pickOffsets[pickIndex++];
  741. vertexArray = primitive._va[pickOffset.index];
  742. }
  743. // Stencil depth command
  744. command = pickCommands[j];
  745. if (!defined(command)) {
  746. command = pickCommands[j] = new DrawCommand({
  747. owner: classificationPrimitive,
  748. primitiveType: primitive._primitiveType,
  749. pickOnly: true,
  750. });
  751. }
  752. command.vertexArray = vertexArray;
  753. command.renderState = classificationPrimitive._rsStencilDepthPass;
  754. command.shaderProgram = classificationPrimitive._sp;
  755. command.uniformMap = uniformMap;
  756. command.pass = Pass.TERRAIN_CLASSIFICATION;
  757. if (usePickOffsets) {
  758. command.offset = pickOffset.offset;
  759. command.count = pickOffset.count;
  760. }
  761. // Derive for 3D Tiles classification
  762. derivedCommand = DrawCommand.shallowClone(
  763. command,
  764. command.derivedCommands.tileset
  765. );
  766. derivedCommand.renderState =
  767. classificationPrimitive._rsStencilDepthPass3DTiles;
  768. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  769. command.derivedCommands.tileset = derivedCommand;
  770. // Pick color command
  771. command = pickCommands[j + 1];
  772. if (!defined(command)) {
  773. command = pickCommands[j + 1] = new DrawCommand({
  774. owner: classificationPrimitive,
  775. primitiveType: primitive._primitiveType,
  776. pickOnly: true,
  777. });
  778. }
  779. command.vertexArray = vertexArray;
  780. command.renderState = classificationPrimitive._rsPickPass;
  781. command.shaderProgram = classificationPrimitive._spPick;
  782. command.uniformMap = uniformMap;
  783. command.pass = Pass.TERRAIN_CLASSIFICATION;
  784. if (usePickOffsets) {
  785. command.offset = pickOffset.offset;
  786. command.count = pickOffset.count;
  787. }
  788. derivedCommand = DrawCommand.shallowClone(
  789. command,
  790. command.derivedCommands.tileset
  791. );
  792. derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
  793. command.derivedCommands.tileset = derivedCommand;
  794. // Derive for 2D if texture coordinates are ever computed
  795. if (needs2DShader) {
  796. // First derive from the terrain command
  797. var derived2DCommand = DrawCommand.shallowClone(
  798. command,
  799. command.derivedCommands.pick2D
  800. );
  801. derived2DCommand.shaderProgram = classificationPrimitive._spPick2D;
  802. command.derivedCommands.pick2D = derived2DCommand;
  803. // Then derive from the 3D Tiles command
  804. derived2DCommand = DrawCommand.shallowClone(
  805. derivedCommand,
  806. derivedCommand.derivedCommands.pick2D
  807. );
  808. derived2DCommand.shaderProgram = classificationPrimitive._spPick2D;
  809. derivedCommand.derivedCommands.pick2D = derived2DCommand;
  810. }
  811. }
  812. }
  813. function createCommands(
  814. classificationPrimitive,
  815. appearance,
  816. material,
  817. translucent,
  818. twoPasses,
  819. colorCommands,
  820. pickCommands
  821. ) {
  822. createColorCommands(classificationPrimitive, colorCommands);
  823. createPickCommands(classificationPrimitive, pickCommands);
  824. }
  825. function boundingVolumeIndex(commandIndex, length) {
  826. return Math.floor((commandIndex % length) / 2);
  827. }
  828. function updateAndQueueRenderCommand(
  829. command,
  830. frameState,
  831. modelMatrix,
  832. cull,
  833. boundingVolume,
  834. debugShowBoundingVolume
  835. ) {
  836. command.modelMatrix = modelMatrix;
  837. command.boundingVolume = boundingVolume;
  838. command.cull = cull;
  839. command.debugShowBoundingVolume = debugShowBoundingVolume;
  840. frameState.commandList.push(command);
  841. }
  842. function updateAndQueuePickCommand(
  843. command,
  844. frameState,
  845. modelMatrix,
  846. cull,
  847. boundingVolume
  848. ) {
  849. command.modelMatrix = modelMatrix;
  850. command.boundingVolume = boundingVolume;
  851. command.cull = cull;
  852. frameState.commandList.push(command);
  853. }
  854. function updateAndQueueCommands(
  855. classificationPrimitive,
  856. frameState,
  857. colorCommands,
  858. pickCommands,
  859. modelMatrix,
  860. cull,
  861. debugShowBoundingVolume,
  862. twoPasses
  863. ) {
  864. var primitive = classificationPrimitive._primitive;
  865. Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix);
  866. var boundingVolumes;
  867. if (frameState.mode === SceneMode.SCENE3D) {
  868. boundingVolumes = primitive._boundingSphereWC;
  869. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  870. boundingVolumes = primitive._boundingSphereCV;
  871. } else if (
  872. frameState.mode === SceneMode.SCENE2D &&
  873. defined(primitive._boundingSphere2D)
  874. ) {
  875. boundingVolumes = primitive._boundingSphere2D;
  876. } else if (defined(primitive._boundingSphereMorph)) {
  877. boundingVolumes = primitive._boundingSphereMorph;
  878. }
  879. var classificationType = classificationPrimitive.classificationType;
  880. var queueTerrainCommands =
  881. classificationType !== ClassificationType.CESIUM_3D_TILE;
  882. var queue3DTilesCommands = classificationType !== ClassificationType.TERRAIN;
  883. var passes = frameState.passes;
  884. var i;
  885. var boundingVolume;
  886. var command;
  887. if (passes.render) {
  888. var colorLength = colorCommands.length;
  889. for (i = 0; i < colorLength; ++i) {
  890. boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)];
  891. if (queueTerrainCommands) {
  892. command = colorCommands[i];
  893. updateAndQueueRenderCommand(
  894. command,
  895. frameState,
  896. modelMatrix,
  897. cull,
  898. boundingVolume,
  899. debugShowBoundingVolume
  900. );
  901. }
  902. if (queue3DTilesCommands) {
  903. command = colorCommands[i].derivedCommands.tileset;
  904. updateAndQueueRenderCommand(
  905. command,
  906. frameState,
  907. modelMatrix,
  908. cull,
  909. boundingVolume,
  910. debugShowBoundingVolume
  911. );
  912. }
  913. }
  914. if (frameState.invertClassification) {
  915. var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow;
  916. var ignoreShowCommandsLength = ignoreShowCommands.length;
  917. for (i = 0; i < ignoreShowCommandsLength; ++i) {
  918. boundingVolume = boundingVolumes[i];
  919. command = ignoreShowCommands[i];
  920. updateAndQueueRenderCommand(
  921. command,
  922. frameState,
  923. modelMatrix,
  924. cull,
  925. boundingVolume,
  926. debugShowBoundingVolume
  927. );
  928. }
  929. }
  930. }
  931. if (passes.pick) {
  932. var pickLength = pickCommands.length;
  933. var pickOffsets = primitive._pickOffsets;
  934. for (i = 0; i < pickLength; ++i) {
  935. var pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)];
  936. boundingVolume = boundingVolumes[pickOffset.index];
  937. if (queueTerrainCommands) {
  938. command = pickCommands[i];
  939. updateAndQueuePickCommand(
  940. command,
  941. frameState,
  942. modelMatrix,
  943. cull,
  944. boundingVolume
  945. );
  946. }
  947. if (queue3DTilesCommands) {
  948. command = pickCommands[i].derivedCommands.tileset;
  949. updateAndQueuePickCommand(
  950. command,
  951. frameState,
  952. modelMatrix,
  953. cull,
  954. boundingVolume
  955. );
  956. }
  957. }
  958. }
  959. }
  960. /**
  961. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  962. * get the draw commands needed to render this primitive.
  963. * <p>
  964. * Do not call this function directly. This is documented just to
  965. * list the exceptions that may be propagated when the scene is rendered:
  966. * </p>
  967. *
  968. * @exception {DeveloperError} All instance geometries must have the same primitiveType.
  969. * @exception {DeveloperError} Appearance and material have a uniform with the same name.
  970. * @exception {DeveloperError} Not all of the geometry instances have the same color attribute.
  971. */
  972. ClassificationPrimitive.prototype.update = function (frameState) {
  973. if (!defined(this._primitive) && !defined(this.geometryInstances)) {
  974. return;
  975. }
  976. var appearance = this.appearance;
  977. if (defined(appearance) && defined(appearance.material)) {
  978. appearance.material.update(frameState.context);
  979. }
  980. var that = this;
  981. var primitiveOptions = this._primitiveOptions;
  982. if (!defined(this._primitive)) {
  983. var instances = Array.isArray(this.geometryInstances)
  984. ? this.geometryInstances
  985. : [this.geometryInstances];
  986. var length = instances.length;
  987. var i;
  988. var instance;
  989. var attributes;
  990. var hasPerColorAttribute = false;
  991. var allColorsSame = true;
  992. var firstColor;
  993. var hasSphericalExtentsAttribute = false;
  994. var hasPlanarExtentsAttributes = false;
  995. if (length > 0) {
  996. attributes = instances[0].attributes;
  997. // Not expecting these to be set by users, should only be set via GroundPrimitive.
  998. // So don't check for mismatch.
  999. hasSphericalExtentsAttribute = ShadowVolumeAppearance.hasAttributesForSphericalExtents(
  1000. attributes
  1001. );
  1002. hasPlanarExtentsAttributes = ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(
  1003. attributes
  1004. );
  1005. firstColor = attributes.color;
  1006. }
  1007. for (i = 0; i < length; i++) {
  1008. instance = instances[i];
  1009. var color = instance.attributes.color;
  1010. if (defined(color)) {
  1011. hasPerColorAttribute = true;
  1012. }
  1013. //>>includeStart('debug', pragmas.debug);
  1014. else if (hasPerColorAttribute) {
  1015. throw new DeveloperError(
  1016. "All GeometryInstances must have color attributes to use per-instance color."
  1017. );
  1018. }
  1019. //>>includeEnd('debug');
  1020. allColorsSame =
  1021. allColorsSame &&
  1022. defined(color) &&
  1023. ColorGeometryInstanceAttribute.equals(firstColor, color);
  1024. }
  1025. // If no attributes exist for computing spherical extents or fragment culling,
  1026. // throw if the colors aren't all the same.
  1027. if (
  1028. !allColorsSame &&
  1029. !hasSphericalExtentsAttribute &&
  1030. !hasPlanarExtentsAttributes
  1031. ) {
  1032. throw new DeveloperError(
  1033. "All GeometryInstances must have the same color attribute except via GroundPrimitives"
  1034. );
  1035. }
  1036. // default to a color appearance
  1037. if (hasPerColorAttribute && !defined(appearance)) {
  1038. appearance = new PerInstanceColorAppearance({
  1039. flat: true,
  1040. });
  1041. this.appearance = appearance;
  1042. }
  1043. //>>includeStart('debug', pragmas.debug);
  1044. if (
  1045. !hasPerColorAttribute &&
  1046. appearance instanceof PerInstanceColorAppearance
  1047. ) {
  1048. throw new DeveloperError(
  1049. "PerInstanceColorAppearance requires color GeometryInstanceAttributes on all GeometryInstances"
  1050. );
  1051. }
  1052. if (
  1053. defined(appearance.material) &&
  1054. !hasSphericalExtentsAttribute &&
  1055. !hasPlanarExtentsAttributes
  1056. ) {
  1057. throw new DeveloperError(
  1058. "Materials on ClassificationPrimitives are not supported except via GroundPrimitives"
  1059. );
  1060. }
  1061. //>>includeEnd('debug');
  1062. this._usePickOffsets =
  1063. !hasSphericalExtentsAttribute && !hasPlanarExtentsAttributes;
  1064. this._hasSphericalExtentsAttribute = hasSphericalExtentsAttribute;
  1065. this._hasPlanarExtentsAttributes = hasPlanarExtentsAttributes;
  1066. this._hasPerColorAttribute = hasPerColorAttribute;
  1067. var geometryInstances = new Array(length);
  1068. for (i = 0; i < length; ++i) {
  1069. instance = instances[i];
  1070. geometryInstances[i] = new GeometryInstance({
  1071. geometry: instance.geometry,
  1072. attributes: instance.attributes,
  1073. modelMatrix: instance.modelMatrix,
  1074. id: instance.id,
  1075. pickPrimitive: defaultValue(this._pickPrimitive, that),
  1076. });
  1077. }
  1078. primitiveOptions.appearance = appearance;
  1079. primitiveOptions.geometryInstances = geometryInstances;
  1080. if (defined(this._createBoundingVolumeFunction)) {
  1081. primitiveOptions._createBoundingVolumeFunction = function (
  1082. frameState,
  1083. geometry
  1084. ) {
  1085. that._createBoundingVolumeFunction(frameState, geometry);
  1086. };
  1087. }
  1088. primitiveOptions._createRenderStatesFunction = function (
  1089. primitive,
  1090. context,
  1091. appearance,
  1092. twoPasses
  1093. ) {
  1094. createRenderStates(that, context);
  1095. };
  1096. primitiveOptions._createShaderProgramFunction = function (
  1097. primitive,
  1098. frameState,
  1099. appearance
  1100. ) {
  1101. createShaderProgram(that, frameState);
  1102. };
  1103. primitiveOptions._createCommandsFunction = function (
  1104. primitive,
  1105. appearance,
  1106. material,
  1107. translucent,
  1108. twoPasses,
  1109. colorCommands,
  1110. pickCommands
  1111. ) {
  1112. createCommands(
  1113. that,
  1114. undefined,
  1115. undefined,
  1116. true,
  1117. false,
  1118. colorCommands,
  1119. pickCommands
  1120. );
  1121. };
  1122. if (defined(this._updateAndQueueCommandsFunction)) {
  1123. primitiveOptions._updateAndQueueCommandsFunction = function (
  1124. primitive,
  1125. frameState,
  1126. colorCommands,
  1127. pickCommands,
  1128. modelMatrix,
  1129. cull,
  1130. debugShowBoundingVolume,
  1131. twoPasses
  1132. ) {
  1133. that._updateAndQueueCommandsFunction(
  1134. primitive,
  1135. frameState,
  1136. colorCommands,
  1137. pickCommands,
  1138. modelMatrix,
  1139. cull,
  1140. debugShowBoundingVolume,
  1141. twoPasses
  1142. );
  1143. };
  1144. } else {
  1145. primitiveOptions._updateAndQueueCommandsFunction = function (
  1146. primitive,
  1147. frameState,
  1148. colorCommands,
  1149. pickCommands,
  1150. modelMatrix,
  1151. cull,
  1152. debugShowBoundingVolume,
  1153. twoPasses
  1154. ) {
  1155. updateAndQueueCommands(
  1156. that,
  1157. frameState,
  1158. colorCommands,
  1159. pickCommands,
  1160. modelMatrix,
  1161. cull,
  1162. debugShowBoundingVolume,
  1163. twoPasses
  1164. );
  1165. };
  1166. }
  1167. this._primitive = new Primitive(primitiveOptions);
  1168. this._primitive.readyPromise.then(function (primitive) {
  1169. that._ready = true;
  1170. if (that.releaseGeometryInstances) {
  1171. that.geometryInstances = undefined;
  1172. }
  1173. var error = primitive._error;
  1174. if (!defined(error)) {
  1175. that._readyPromise.resolve(that);
  1176. } else {
  1177. that._readyPromise.reject(error);
  1178. }
  1179. });
  1180. }
  1181. if (
  1182. this.debugShowShadowVolume &&
  1183. !this._debugShowShadowVolume &&
  1184. this._ready
  1185. ) {
  1186. this._debugShowShadowVolume = true;
  1187. this._rsStencilDepthPass = RenderState.fromCache(
  1188. getStencilDepthRenderState(false, false)
  1189. );
  1190. this._rsStencilDepthPass3DTiles = RenderState.fromCache(
  1191. getStencilDepthRenderState(false, true)
  1192. );
  1193. this._rsColorPass = RenderState.fromCache(getColorRenderState(false));
  1194. } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) {
  1195. this._debugShowShadowVolume = false;
  1196. this._rsStencilDepthPass = RenderState.fromCache(
  1197. getStencilDepthRenderState(true, false)
  1198. );
  1199. this._rsStencilDepthPass3DTiles = RenderState.fromCache(
  1200. getStencilDepthRenderState(true, true)
  1201. );
  1202. this._rsColorPass = RenderState.fromCache(getColorRenderState(true));
  1203. }
  1204. // Update primitive appearance
  1205. if (this._primitive.appearance !== appearance) {
  1206. //>>includeStart('debug', pragmas.debug);
  1207. // Check if the appearance is supported by the geometry attributes
  1208. if (
  1209. !this._hasSphericalExtentsAttribute &&
  1210. !this._hasPlanarExtentsAttributes &&
  1211. defined(appearance.material)
  1212. ) {
  1213. throw new DeveloperError(
  1214. "Materials on ClassificationPrimitives are not supported except via GroundPrimitive"
  1215. );
  1216. }
  1217. if (
  1218. !this._hasPerColorAttribute &&
  1219. appearance instanceof PerInstanceColorAppearance
  1220. ) {
  1221. throw new DeveloperError(
  1222. "PerInstanceColorAppearance requires color GeometryInstanceAttribute"
  1223. );
  1224. }
  1225. //>>includeEnd('debug');
  1226. this._primitive.appearance = appearance;
  1227. }
  1228. this._primitive.show = this.show;
  1229. this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
  1230. this._primitive.update(frameState);
  1231. };
  1232. /**
  1233. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  1234. *
  1235. * @param {*} id The id of the {@link GeometryInstance}.
  1236. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id.
  1237. *
  1238. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  1239. *
  1240. * @example
  1241. * var attributes = primitive.getGeometryInstanceAttributes('an id');
  1242. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  1243. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  1244. */
  1245. ClassificationPrimitive.prototype.getGeometryInstanceAttributes = function (
  1246. id
  1247. ) {
  1248. //>>includeStart('debug', pragmas.debug);
  1249. if (!defined(this._primitive)) {
  1250. throw new DeveloperError(
  1251. "must call update before calling getGeometryInstanceAttributes"
  1252. );
  1253. }
  1254. //>>includeEnd('debug');
  1255. return this._primitive.getGeometryInstanceAttributes(id);
  1256. };
  1257. /**
  1258. * Returns true if this object was destroyed; otherwise, false.
  1259. * <p>
  1260. * If this object was destroyed, it should not be used; calling any function other than
  1261. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1262. * </p>
  1263. *
  1264. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1265. *
  1266. * @see ClassificationPrimitive#destroy
  1267. */
  1268. ClassificationPrimitive.prototype.isDestroyed = function () {
  1269. return false;
  1270. };
  1271. /**
  1272. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1273. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1274. * <p>
  1275. * Once an object is destroyed, it should not be used; calling any function other than
  1276. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1277. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1278. * </p>
  1279. *
  1280. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1281. *
  1282. * @example
  1283. * e = e && e.destroy();
  1284. *
  1285. * @see ClassificationPrimitive#isDestroyed
  1286. */
  1287. ClassificationPrimitive.prototype.destroy = function () {
  1288. this._primitive = this._primitive && this._primitive.destroy();
  1289. this._sp = this._sp && this._sp.destroy();
  1290. this._spPick = this._spPick && this._spPick.destroy();
  1291. this._spColor = this._spColor && this._spColor.destroy();
  1292. // Derived programs, destroyed above if they existed.
  1293. this._spPick2D = undefined;
  1294. this._spColor2D = undefined;
  1295. return destroyObject(this);
  1296. };
  1297. export default ClassificationPrimitive;