ClassificationPrimitive.js 55 KB

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