Primitive.js 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317
  1. /*global define*/
  2. define([
  3. '../Core/BoundingSphere',
  4. '../Core/clone',
  5. '../Core/combine',
  6. '../Core/ComponentDatatype',
  7. '../Core/defaultValue',
  8. '../Core/defined',
  9. '../Core/defineProperties',
  10. '../Core/destroyObject',
  11. '../Core/DeveloperError',
  12. '../Core/FeatureDetection',
  13. '../Core/Geometry',
  14. '../Core/GeometryAttribute',
  15. '../Core/GeometryAttributes',
  16. '../Core/GeometryInstance',
  17. '../Core/GeometryInstanceAttribute',
  18. '../Core/isArray',
  19. '../Core/Matrix4',
  20. '../Core/subdivideArray',
  21. '../Core/TaskProcessor',
  22. '../Renderer/BufferUsage',
  23. '../Renderer/DrawCommand',
  24. '../Renderer/ShaderSource',
  25. '../ThirdParty/when',
  26. './CullFace',
  27. './Pass',
  28. './PrimitivePipeline',
  29. './PrimitiveState',
  30. './SceneMode'
  31. ], function(
  32. BoundingSphere,
  33. clone,
  34. combine,
  35. ComponentDatatype,
  36. defaultValue,
  37. defined,
  38. defineProperties,
  39. destroyObject,
  40. DeveloperError,
  41. FeatureDetection,
  42. Geometry,
  43. GeometryAttribute,
  44. GeometryAttributes,
  45. GeometryInstance,
  46. GeometryInstanceAttribute,
  47. isArray,
  48. Matrix4,
  49. subdivideArray,
  50. TaskProcessor,
  51. BufferUsage,
  52. DrawCommand,
  53. ShaderSource,
  54. when,
  55. CullFace,
  56. Pass,
  57. PrimitivePipeline,
  58. PrimitiveState,
  59. SceneMode) {
  60. "use strict";
  61. /**
  62. * A primitive represents geometry in the {@link Scene}. The geometry can be from a single {@link GeometryInstance}
  63. * as shown in example 1 below, or from an array of instances, even if the geometry is from different
  64. * geometry types, e.g., an {@link RectangleGeometry} and an {@link EllipsoidGeometry} as shown in Code Example 2.
  65. * <p>
  66. * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including
  67. * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement,
  68. * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix
  69. * and match most of them and add a new geometry or appearance independently of each other.
  70. * </p>
  71. * <p>
  72. * Combining multiple instances into one primitive is called batching, and significantly improves performance for static data.
  73. * Instances can be individually picked; {@link Scene#pick} returns their {@link GeometryInstance#id}. Using
  74. * per-instance appearances like {@link PerInstanceColorAppearance}, each instance can also have a unique color.
  75. * </p>
  76. * <p>
  77. * {@link Geometry} can either be created and batched on a web worker or the main thread. The first two examples
  78. * show geometry that will be created on a web worker by using the descriptions of the geometry. The third example
  79. * shows how to create the geometry on the main thread by explicitly calling the <code>createGeometry</code> method.
  80. * </p>
  81. *
  82. * @alias Primitive
  83. * @constructor
  84. *
  85. * @param {Object} [options] Object with the following properties:
  86. * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances - or a single geometry instance - to render.
  87. * @param {Appearance} [options.appearance] The appearance used to render the primitive.
  88. * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
  89. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates.
  90. * @param {Boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  91. * @param {Boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
  92. * @param {Boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory.
  93. * @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.
  94. * @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.
  95. * @param {Boolean} [options.cull=true] When <code>true</code>, the renderer frustum culls and horizon culls the primitive's commands based on their bounding volume. Set this to <code>false</code> for a small performance gain if you are manually culling the primitive.
  96. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready.
  97. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  98. *
  99. * @see GeometryInstance
  100. * @see Appearance
  101. *
  102. * @example
  103. * // 1. Draw a translucent ellipse on the surface with a checkerboard pattern
  104. * var instance = new Cesium.GeometryInstance({
  105. * geometry : new Cesium.EllipseGeometry({
  106. * center : Cesium.Cartesian3.fromDegrees(-100.0, 20.0),
  107. * semiMinorAxis : 500000.0,
  108. * semiMajorAxis : 1000000.0,
  109. * rotation : Cesium.Math.PI_OVER_FOUR,
  110. * vertexFormat : Cesium.VertexFormat.POSITION_AND_ST
  111. * }),
  112. * id : 'object returned when this instance is picked and to get/set per-instance attributes'
  113. * });
  114. * scene.primitives.add(new Cesium.Primitive({
  115. * geometryInstances : instance,
  116. * appearance : new Cesium.EllipsoidSurfaceAppearance({
  117. * material : Cesium.Material.fromType('Checkerboard')
  118. * })
  119. * }));
  120. *
  121. * @example
  122. * // 2. Draw different instances each with a unique color
  123. * var rectangleInstance = new Cesium.GeometryInstance({
  124. * geometry : new Cesium.RectangleGeometry({
  125. * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0),
  126. * vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
  127. * }),
  128. * id : 'rectangle',
  129. * attributes : {
  130. * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
  131. * }
  132. * });
  133. * var ellipsoidInstance = new Cesium.GeometryInstance({
  134. * geometry : new Cesium.EllipsoidGeometry({
  135. * radii : new Cesium.Cartesian3(500000.0, 500000.0, 1000000.0),
  136. * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL
  137. * }),
  138. * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
  139. * Cesium.Cartesian3.fromDegrees(-95.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 500000.0), new Cesium.Matrix4()),
  140. * id : 'ellipsoid',
  141. * attributes : {
  142. * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA)
  143. * }
  144. * });
  145. * scene.primitives.add(new Cesium.Primitive({
  146. * geometryInstances : [rectangleInstance, ellipsoidInstance],
  147. * appearance : new Cesium.PerInstanceColorAppearance()
  148. * }));
  149. *
  150. * @example
  151. * // 3. Create the geometry on the main thread.
  152. * scene.primitives.add(new Cesium.Primitive({
  153. * geometryInstances : new Cesium.GeometryInstance({
  154. * geometry : Cesium.EllipsoidGeometry.createGeometry(new Cesium.EllipsoidGeometry({
  155. * radii : new Cesium.Cartesian3(500000.0, 500000.0, 1000000.0),
  156. * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL
  157. * })),
  158. * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
  159. * Cesium.Cartesian3.fromDegrees(-95.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 500000.0), new Cesium.Matrix4()),
  160. * id : 'ellipsoid',
  161. * attributes : {
  162. * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA)
  163. * }
  164. * }),
  165. * appearance : new Cesium.PerInstanceColorAppearance()
  166. * }));
  167. */
  168. var Primitive = function(options) {
  169. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  170. /**
  171. * The geometry instances rendered with this primitive. This may
  172. * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>
  173. * is <code>true</code> when the primitive is constructed.
  174. * <p>
  175. * Changing this property after the primitive is rendered has no effect.
  176. * </p>
  177. *
  178. * @type Array
  179. *
  180. * @default undefined
  181. */
  182. this.geometryInstances = options.geometryInstances;
  183. /**
  184. * The {@link Appearance} used to shade this primitive. Each geometry
  185. * instance is shaded with the same appearance. Some appearances, like
  186. * {@link PerInstanceColorAppearance} allow giving each instance unique
  187. * properties.
  188. *
  189. * @type Appearance
  190. *
  191. * @default undefined
  192. */
  193. this.appearance = options.appearance;
  194. this._appearance = undefined;
  195. this._material = undefined;
  196. /**
  197. * The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates.
  198. * When this is the identity matrix, the primitive is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  199. * Local reference frames can be used by providing a different transformation matrix, like that returned
  200. * by {@link Transforms.eastNorthUpToFixedFrame}.
  201. *
  202. * <p>
  203. * If the model matrix is changed after creation, it only affects primitives with one instance and only in 3D mode.
  204. * </p>
  205. *
  206. * @type Matrix4
  207. *
  208. * @default Matrix4.IDENTITY
  209. *
  210. * @example
  211. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  212. * p.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  213. */
  214. this.modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
  215. this._modelMatrix = new Matrix4();
  216. /**
  217. * Determines if the primitive will be shown. This affects all geometry
  218. * instances in the primitive.
  219. *
  220. * @type Boolean
  221. *
  222. * @default true
  223. */
  224. this.show = defaultValue(options.show, true);
  225. this._vertexCacheOptimize = defaultValue(options.vertexCacheOptimize, false);
  226. this._interleave = defaultValue(options.interleave, false);
  227. this._releaseGeometryInstances = defaultValue(options.releaseGeometryInstances, true);
  228. this._allowPicking = defaultValue(options.allowPicking, true);
  229. this._asynchronous = defaultValue(options.asynchronous, true);
  230. this._compressVertices = defaultValue(options.compressVertices, true);
  231. /**
  232. * When <code>true</code>, the renderer frustum culls and horizon culls the primitive's commands
  233. * based on their bounding volume. Set this to <code>false</code> for a small performance gain
  234. * if you are manually culling the primitive.
  235. *
  236. * @type {Boolean}
  237. *
  238. * @default true
  239. */
  240. this.cull = defaultValue(options.cull, true);
  241. /**
  242. * This property is for debugging only; it is not for production use nor is it optimized.
  243. * <p>
  244. * Draws the bounding sphere for each draw command in the primitive.
  245. * </p>
  246. *
  247. * @type {Boolean}
  248. *
  249. * @default false
  250. */
  251. this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false);
  252. this._translucent = undefined;
  253. this._state = PrimitiveState.READY;
  254. this._geometries = [];
  255. this._vaAttributes = undefined;
  256. this._error = undefined;
  257. this._numberOfInstances = 0;
  258. this._validModelMatrix = false;
  259. this._boundingSpheres = [];
  260. this._boundingSphereWC = [];
  261. this._boundingSphereCV = [];
  262. this._boundingSphere2D = [];
  263. this._boundingSphereMorph = [];
  264. this._perInstanceAttributeLocations = undefined;
  265. this._instanceIds = [];
  266. this._lastPerInstanceAttributeIndex = 0;
  267. this._dirtyAttributes = [];
  268. this._va = [];
  269. this._attributeLocations = undefined;
  270. this._primitiveType = undefined;
  271. this._frontFaceRS = undefined;
  272. this._backFaceRS = undefined;
  273. this._sp = undefined;
  274. this._pickRS = undefined;
  275. this._pickSP = undefined;
  276. this._pickIds = [];
  277. this._colorCommands = [];
  278. this._pickCommands = [];
  279. this._createGeometryResults = undefined;
  280. };
  281. defineProperties(Primitive.prototype, {
  282. /**
  283. * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  284. *
  285. * @memberof Primitive.prototype
  286. *
  287. * @type {Boolean}
  288. * @readonly
  289. *
  290. * @default true
  291. */
  292. vertexCacheOptimize : {
  293. get : function() {
  294. return this._vertexCacheOptimize;
  295. }
  296. },
  297. /**
  298. * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
  299. *
  300. * @memberof Primitive.prototype
  301. *
  302. * @type {Boolean}
  303. * @readonly
  304. *
  305. * @default false
  306. */
  307. interleave : {
  308. get : function() {
  309. return this._interleave;
  310. }
  311. },
  312. /**
  313. * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  314. *
  315. * @memberof Primitive.prototype
  316. *
  317. * @type {Boolean}
  318. * @readonly
  319. *
  320. * @default true
  321. */
  322. releaseGeometryInstances : {
  323. get : function() {
  324. return this._releaseGeometryInstances;
  325. }
  326. },
  327. /**
  328. * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved. *
  329. *
  330. * @memberof Primitive.prototype
  331. *
  332. * @type {Boolean}
  333. * @readonly
  334. *
  335. * @default true
  336. */
  337. allowPicking : {
  338. get : function() {
  339. return this._allowPicking;
  340. }
  341. },
  342. /**
  343. * Determines if the geometry instances will be created and batched on a web worker.
  344. *
  345. * @memberof Primitive.prototype
  346. *
  347. * @type {Boolean}
  348. * @readonly
  349. *
  350. * @default true
  351. */
  352. asynchronous : {
  353. get : function() {
  354. return this._asynchronous;
  355. }
  356. },
  357. /**
  358. * When <code>true</code>, geometry vertices are compressed, which will save memory.
  359. *
  360. * @memberof Primitive.prototype
  361. *
  362. * @type {Boolean}
  363. * @readonly
  364. *
  365. * @default true
  366. */
  367. compressVertices : {
  368. get : function() {
  369. return this._compressVertices;
  370. }
  371. },
  372. /**
  373. * Determines if the primitive is complete and ready to render. If this property is
  374. * true, the primitive will be rendered the next time that {@link Primitive#update}
  375. * is called.
  376. *
  377. * @memberof Primitive.prototype
  378. *
  379. * @type {Boolean}
  380. * @readonly
  381. */
  382. ready : {
  383. get : function() {
  384. return this._state === PrimitiveState.COMPLETE;
  385. }
  386. }
  387. });
  388. function cloneAttribute(attribute) {
  389. return new GeometryAttribute({
  390. componentDatatype : attribute.componentDatatype,
  391. componentsPerAttribute : attribute.componentsPerAttribute,
  392. normalize : attribute.normalize,
  393. values : new attribute.values.constructor(attribute.values)
  394. });
  395. }
  396. function cloneGeometry(geometry) {
  397. var attributes = geometry.attributes;
  398. var newAttributes = new GeometryAttributes();
  399. for (var property in attributes) {
  400. if (attributes.hasOwnProperty(property) && defined(attributes[property])) {
  401. newAttributes[property] = cloneAttribute(attributes[property]);
  402. }
  403. }
  404. var indices;
  405. if (defined(geometry.indices)) {
  406. var sourceValues = geometry.indices;
  407. indices = new sourceValues.constructor(sourceValues);
  408. }
  409. return new Geometry({
  410. attributes : newAttributes,
  411. indices : indices,
  412. primitiveType : geometry.primitiveType,
  413. boundingSphere : BoundingSphere.clone(geometry.boundingSphere)
  414. });
  415. }
  416. function cloneGeometryInstanceAttribute(attribute) {
  417. return new GeometryInstanceAttribute({
  418. componentDatatype : attribute.componentDatatype,
  419. componentsPerAttribute : attribute.componentsPerAttribute,
  420. normalize : attribute.normalize,
  421. value : new attribute.value.constructor(attribute.value)
  422. });
  423. }
  424. function cloneInstance(instance, geometry) {
  425. var attributes = instance.attributes;
  426. var newAttributes = {};
  427. for (var property in attributes) {
  428. if (attributes.hasOwnProperty(property)) {
  429. newAttributes[property] = cloneGeometryInstanceAttribute(attributes[property]);
  430. }
  431. }
  432. return new GeometryInstance({
  433. geometry : geometry,
  434. modelMatrix : Matrix4.clone(instance.modelMatrix),
  435. attributes : newAttributes
  436. });
  437. }
  438. var positionRegex = /attribute\s+vec(?:3|4)\s+(.*)3DHigh;/g;
  439. function createColumbusViewShader(primitive, vertexShaderSource, scene3DOnly) {
  440. var match;
  441. var forwardDecl = '';
  442. var attributes = '';
  443. var computeFunctions = '';
  444. while ((match = positionRegex.exec(vertexShaderSource)) !== null) {
  445. var name = match[1];
  446. var functionName = 'vec4 czm_compute' + name[0].toUpperCase() + name.substr(1) + '()';
  447. // Don't forward-declare czm_computePosition because computePosition.glsl already does.
  448. if (functionName !== 'vec4 czm_computePosition()') {
  449. forwardDecl += functionName + ';\n';
  450. }
  451. if (!scene3DOnly) {
  452. attributes +=
  453. 'attribute vec3 ' + name + '2DHigh;\n' +
  454. 'attribute vec3 ' + name + '2DLow;\n';
  455. computeFunctions +=
  456. functionName + '\n' +
  457. '{\n' +
  458. ' vec4 p;\n' +
  459. ' if (czm_morphTime == 1.0)\n' +
  460. ' {\n' +
  461. ' p = czm_translateRelativeToEye(' + name + '3DHigh, ' + name + '3DLow);\n' +
  462. ' }\n' +
  463. ' else if (czm_morphTime == 0.0)\n' +
  464. ' {\n' +
  465. ' p = czm_translateRelativeToEye(' + name + '2DHigh.zxy, ' + name + '2DLow.zxy);\n' +
  466. ' }\n' +
  467. ' else\n' +
  468. ' {\n' +
  469. ' p = czm_columbusViewMorph(\n' +
  470. ' czm_translateRelativeToEye(' + name + '2DHigh.zxy, ' + name + '2DLow.zxy),\n' +
  471. ' czm_translateRelativeToEye(' + name + '3DHigh, ' + name + '3DLow),\n' +
  472. ' czm_morphTime);\n' +
  473. ' }\n' +
  474. ' return p;\n' +
  475. '}\n\n';
  476. } else {
  477. computeFunctions +=
  478. functionName + '\n' +
  479. '{\n' +
  480. ' return czm_translateRelativeToEye(' + name + '3DHigh, ' + name + '3DLow);\n' +
  481. '}\n\n';
  482. }
  483. }
  484. return [forwardDecl, attributes, vertexShaderSource, computeFunctions].join('\n');
  485. }
  486. function createPickVertexShaderSource(vertexShaderSource) {
  487. var renamedVS = vertexShaderSource.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_old_main()');
  488. var pickMain =
  489. 'attribute vec4 pickColor; \n' +
  490. 'varying vec4 czm_pickColor; \n' +
  491. 'void main() \n' +
  492. '{ \n' +
  493. ' czm_old_main(); \n' +
  494. ' czm_pickColor = pickColor; \n' +
  495. '}';
  496. return renamedVS + '\n' + pickMain;
  497. }
  498. function appendShow(primitive, vertexShaderSource) {
  499. if (!defined(primitive._attributeLocations.show)) {
  500. return vertexShaderSource;
  501. }
  502. var renamedVS = vertexShaderSource.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_non_show_main()');
  503. var showMain =
  504. 'attribute float show;\n' +
  505. 'void main() \n' +
  506. '{ \n' +
  507. ' czm_non_show_main(); \n' +
  508. ' gl_Position *= show; \n' +
  509. '}';
  510. return renamedVS + '\n' + showMain;
  511. }
  512. function modifyForEncodedNormals(primitive, vertexShaderSource) {
  513. if (!primitive.compressVertices) {
  514. return vertexShaderSource;
  515. }
  516. var containsNormal = vertexShaderSource.search(/attribute\s+vec3\s+normal;/g) !== -1;
  517. var containsSt = vertexShaderSource.search(/attribute\s+vec2\s+st;/g) !== -1;
  518. if (!containsNormal && !containsSt) {
  519. return vertexShaderSource;
  520. }
  521. var containsTangent = vertexShaderSource.search(/attribute\s+vec3\s+tangent;/g) !== -1;
  522. var containsBinormal = vertexShaderSource.search(/attribute\s+vec3\s+binormal;/g) !== -1;
  523. var numComponents = containsSt && containsNormal ? 2.0 : 1.0;
  524. numComponents += containsTangent || containsBinormal ? 1 : 0;
  525. var type = (numComponents > 1) ? 'vec' + numComponents : 'float';
  526. var attributeName = 'compressedAttributes';
  527. var attributeDecl = 'attribute ' + type + ' ' + attributeName + ';';
  528. var globalDecl = '';
  529. var decode = '';
  530. if (containsSt) {
  531. globalDecl += 'vec2 st;\n';
  532. var stComponent = numComponents > 1 ? attributeName + '.x' : attributeName;
  533. decode += ' st = czm_decompressTextureCoordinates(' + stComponent + ');\n';
  534. }
  535. if (containsNormal && containsTangent && containsBinormal) {
  536. globalDecl +=
  537. 'vec3 normal;\n' +
  538. 'vec3 tangent;\n' +
  539. 'vec3 binormal;\n';
  540. decode += ' czm_octDecode(' + attributeName + '.' + (containsSt ? 'yz' : 'xy') + ', normal, tangent, binormal);\n';
  541. } else {
  542. if (containsNormal) {
  543. globalDecl += 'vec3 normal;\n';
  544. decode += ' normal = czm_octDecode(' + attributeName + (numComponents > 1 ? '.' + (containsSt ? 'y' : 'x') : '') + ');\n';
  545. }
  546. if (containsTangent) {
  547. globalDecl += 'vec3 tangent;\n';
  548. decode += ' tangent = czm_octDecode(' + attributeName + '.' + (containsSt && containsNormal ? 'z' : 'y') + ');\n';
  549. }
  550. if (containsBinormal) {
  551. globalDecl += 'vec3 binormal;\n';
  552. decode += ' binormal = czm_octDecode(' + attributeName + '.' + (containsSt && containsNormal ? 'z' : 'y') + ');\n';
  553. }
  554. }
  555. var modifiedVS = vertexShaderSource;
  556. modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+normal;/g, '');
  557. modifiedVS = modifiedVS.replace(/attribute\s+vec2\s+st;/g, '');
  558. modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+tangent;/g, '');
  559. modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+binormal;/g, '');
  560. modifiedVS = modifiedVS.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_non_compressed_main()');
  561. var compressedMain =
  562. 'void main() \n' +
  563. '{ \n' +
  564. decode +
  565. ' czm_non_compressed_main(); \n' +
  566. '}';
  567. return [attributeDecl, globalDecl, modifiedVS, compressedMain].join('\n');
  568. }
  569. function validateShaderMatching(shaderProgram, attributeLocations) {
  570. // For a VAO and shader program to be compatible, the VAO must have
  571. // all active attribute in the shader program. The VAO may have
  572. // extra attributes with the only concern being a potential
  573. // performance hit due to extra memory bandwidth and cache pollution.
  574. // The shader source could have extra attributes that are not used,
  575. // but there is no guarantee they will be optimized out.
  576. //
  577. // Here, we validate that the VAO has all attributes required
  578. // to match the shader program.
  579. var shaderAttributes = shaderProgram.vertexAttributes;
  580. //>>includeStart('debug', pragmas.debug);
  581. for (var name in shaderAttributes) {
  582. if (shaderAttributes.hasOwnProperty(name)) {
  583. if (!defined(attributeLocations[name])) {
  584. throw new DeveloperError('Appearance/Geometry mismatch. The appearance requires vertex shader attribute input \'' + name +
  585. '\', which was not computed as part of the Geometry. Use the appearance\'s vertexFormat property when constructing the geometry.');
  586. }
  587. }
  588. }
  589. //>>includeEnd('debug');
  590. }
  591. function createPickIds(context, primitive, instances) {
  592. var pickColors = [];
  593. var length = instances.length;
  594. for (var i = 0; i < length; ++i) {
  595. var pickObject = {
  596. primitive : defaultValue(instances[i].pickPrimitive, primitive)
  597. };
  598. if (defined(instances[i].id)) {
  599. pickObject.id = instances[i].id;
  600. }
  601. var pickId = context.createPickId(pickObject);
  602. primitive._pickIds.push(pickId);
  603. pickColors.push(pickId.color);
  604. }
  605. return pickColors;
  606. }
  607. function getUniformFunction(uniforms, name) {
  608. return function() {
  609. return uniforms[name];
  610. };
  611. }
  612. var numberOfCreationWorkers = Math.max(FeatureDetection.hardwareConcurrency - 1, 1);
  613. var createGeometryTaskProcessors;
  614. var combineGeometryTaskProcessor = new TaskProcessor('combineGeometry', Number.POSITIVE_INFINITY);
  615. /**
  616. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  617. * get the draw commands needed to render this primitive.
  618. * <p>
  619. * Do not call this function directly. This is documented just to
  620. * list the exceptions that may be propagated when the scene is rendered:
  621. * </p>
  622. *
  623. * @exception {DeveloperError} All instance geometries must have the same primitiveType.
  624. * @exception {DeveloperError} Appearance and material have a uniform with the same name.
  625. */
  626. Primitive.prototype.update = function(context, frameState, commandList) {
  627. if (((!defined(this.geometryInstances)) && (this._va.length === 0)) ||
  628. (defined(this.geometryInstances) && isArray(this.geometryInstances) && this.geometryInstances.length === 0) ||
  629. (!defined(this.appearance)) ||
  630. (frameState.mode !== SceneMode.SCENE3D && frameState.scene3DOnly) ||
  631. (!frameState.passes.render && !frameState.passes.pick)) {
  632. return;
  633. }
  634. var projection = frameState.mapProjection;
  635. var colorCommand;
  636. var pickCommand;
  637. var geometry;
  638. var attributes;
  639. var attribute;
  640. var length;
  641. var i;
  642. var j;
  643. var index;
  644. var promise;
  645. var instances;
  646. var clonedInstances;
  647. var geometries;
  648. var allowPicking = this.allowPicking;
  649. var instanceIds = this._instanceIds;
  650. var scene3DOnly = frameState.scene3DOnly;
  651. var that = this;
  652. if (this._state !== PrimitiveState.COMPLETE && this._state !== PrimitiveState.COMBINED) {
  653. if (this.asynchronous) {
  654. if (this._state === PrimitiveState.FAILED) {
  655. throw this._error;
  656. } else if (this._state === PrimitiveState.READY) {
  657. instances = (isArray(this.geometryInstances)) ? this.geometryInstances : [this.geometryInstances];
  658. this._numberOfInstances = length = instances.length;
  659. var promises = [];
  660. var subTasks = [];
  661. for (i = 0; i < length; ++i) {
  662. geometry = instances[i].geometry;
  663. instanceIds.push(instances[i].id);
  664. subTasks.push({
  665. moduleName : geometry._workerName,
  666. geometry : geometry
  667. });
  668. }
  669. if (!defined(createGeometryTaskProcessors)) {
  670. createGeometryTaskProcessors = new Array(numberOfCreationWorkers);
  671. for (i = 0; i < numberOfCreationWorkers; i++) {
  672. createGeometryTaskProcessors[i] = new TaskProcessor('createGeometry', Number.POSITIVE_INFINITY);
  673. }
  674. }
  675. subTasks = subdivideArray(subTasks, numberOfCreationWorkers);
  676. for (i = 0; i < subTasks.length; i++) {
  677. promises.push(createGeometryTaskProcessors[i].scheduleTask({
  678. subTasks : subTasks[i]
  679. }));
  680. }
  681. this._state = PrimitiveState.CREATING;
  682. when.all(promises, function(results) {
  683. that._createGeometryResults = results;
  684. that._state = PrimitiveState.CREATED;
  685. }, function(error) {
  686. that._error = error;
  687. that._state = PrimitiveState.FAILED;
  688. });
  689. } else if (this._state === PrimitiveState.CREATED) {
  690. var transferableObjects = [];
  691. instances = (isArray(this.geometryInstances)) ? this.geometryInstances : [this.geometryInstances];
  692. promise = combineGeometryTaskProcessor.scheduleTask(PrimitivePipeline.packCombineGeometryParameters({
  693. createGeometryResults : this._createGeometryResults,
  694. instances : instances,
  695. pickIds : allowPicking ? createPickIds(context, this, instances) : undefined,
  696. ellipsoid : projection.ellipsoid,
  697. projection : projection,
  698. elementIndexUintSupported : context.elementIndexUint,
  699. scene3DOnly : scene3DOnly,
  700. allowPicking : allowPicking,
  701. vertexCacheOptimize : this.vertexCacheOptimize,
  702. compressVertices : this.compressVertices,
  703. modelMatrix : this.modelMatrix
  704. }, transferableObjects), transferableObjects);
  705. this._createGeometryResults = undefined;
  706. this._state = PrimitiveState.COMBINING;
  707. when(promise, function(packedResult) {
  708. var result = PrimitivePipeline.unpackCombineGeometryResults(packedResult);
  709. that._geometries = result.geometries;
  710. that._attributeLocations = result.attributeLocations;
  711. that._vaAttributes = result.vaAttributes;
  712. that._perInstanceAttributeLocations = result.perInstanceAttributeLocations;
  713. that._state = PrimitiveState.COMBINED;
  714. that.modelMatrix = Matrix4.clone(result.modelMatrix, that.modelMatrix);
  715. that._validModelMatrix = !Matrix4.equals(that.modelMatrix, Matrix4.IDENTITY);
  716. }, function(error) {
  717. that._error = error;
  718. that._state = PrimitiveState.FAILED;
  719. });
  720. }
  721. } else {
  722. instances = (isArray(this.geometryInstances)) ? this.geometryInstances : [this.geometryInstances];
  723. this._numberOfInstances = length = instances.length;
  724. geometries = new Array(length);
  725. clonedInstances = new Array(instances.length);
  726. for (i = 0; i < length; i++) {
  727. var instance = instances[i];
  728. geometry = instance.geometry;
  729. instanceIds.push(instance.id);
  730. var createdGeometry;
  731. if (defined(geometry.attributes) && defined(geometry.primitiveType)) {
  732. createdGeometry = cloneGeometry(geometry);
  733. } else {
  734. createdGeometry = geometry.constructor.createGeometry(geometry);
  735. }
  736. geometries[i] = createdGeometry;
  737. clonedInstances[i] = cloneInstance(instance, createdGeometry);
  738. }
  739. var result = PrimitivePipeline.combineGeometry({
  740. instances : clonedInstances,
  741. pickIds : allowPicking ? createPickIds(context, this, instances) : undefined,
  742. ellipsoid : projection.ellipsoid,
  743. projection : projection,
  744. elementIndexUintSupported : context.elementIndexUint,
  745. scene3DOnly : scene3DOnly,
  746. allowPicking : allowPicking,
  747. vertexCacheOptimize : this.vertexCacheOptimize,
  748. compressVertices : this.compressVertices,
  749. modelMatrix : this.modelMatrix
  750. });
  751. this._geometries = result.geometries;
  752. this._attributeLocations = result.attributeLocations;
  753. this._vaAttributes = result.vaAttributes;
  754. this._perInstanceAttributeLocations = result.vaAttributeLocations;
  755. this._state = PrimitiveState.COMBINED;
  756. this.modelMatrix = Matrix4.clone(result.modelMatrix, this.modelMatrix);
  757. this._validModelMatrix = !Matrix4.equals(this.modelMatrix, Matrix4.IDENTITY);
  758. }
  759. }
  760. var attributeLocations = this._attributeLocations;
  761. if (this._state === PrimitiveState.COMBINED) {
  762. geometries = this._geometries;
  763. var vaAttributes = this._vaAttributes;
  764. var va = [];
  765. length = geometries.length;
  766. for (i = 0; i < length; ++i) {
  767. geometry = geometries[i];
  768. attributes = vaAttributes[i];
  769. var vaLength = attributes.length;
  770. for (j = 0; j < vaLength; ++j) {
  771. attribute = attributes[j];
  772. attribute.vertexBuffer = context.createVertexBuffer(attribute.values, BufferUsage.DYNAMIC_DRAW);
  773. delete attribute.values;
  774. }
  775. va.push(context.createVertexArrayFromGeometry({
  776. geometry : geometry,
  777. attributeLocations : attributeLocations,
  778. bufferUsage : BufferUsage.STATIC_DRAW,
  779. interleave : this._interleave,
  780. vertexArrayAttributes : attributes
  781. }));
  782. this._boundingSpheres.push(BoundingSphere.clone(geometry.boundingSphere));
  783. this._boundingSphereWC.push(new BoundingSphere());
  784. if (!scene3DOnly) {
  785. var center = geometry.boundingSphereCV.center;
  786. var x = center.x;
  787. var y = center.y;
  788. var z = center.z;
  789. center.x = z;
  790. center.y = x;
  791. center.z = y;
  792. this._boundingSphereCV.push(BoundingSphere.clone(geometry.boundingSphereCV));
  793. this._boundingSphere2D.push(new BoundingSphere());
  794. this._boundingSphereMorph.push(new BoundingSphere());
  795. }
  796. }
  797. this._va = va;
  798. this._primitiveType = geometries[0].primitiveType;
  799. if (this.releaseGeometryInstances) {
  800. this.geometryInstances = undefined;
  801. }
  802. this._geometries = undefined;
  803. this._state = PrimitiveState.COMPLETE;
  804. }
  805. if (!this.show || this._state !== PrimitiveState.COMPLETE) {
  806. return;
  807. }
  808. // Create or recreate render state and shader program if appearance/material changed
  809. var appearance = this.appearance;
  810. var material = appearance.material;
  811. var createRS = false;
  812. var createSP = false;
  813. if (this._appearance !== appearance) {
  814. this._appearance = appearance;
  815. this._material = material;
  816. createRS = true;
  817. createSP = true;
  818. } else if (this._material !== material ) {
  819. this._material = material;
  820. createSP = true;
  821. }
  822. var translucent = this._appearance.isTranslucent();
  823. if (this._translucent !== translucent) {
  824. this._translucent = translucent;
  825. createRS = true;
  826. }
  827. if (defined(this._material)) {
  828. this._material.update(context);
  829. }
  830. var twoPasses = appearance.closed && translucent;
  831. if (createRS) {
  832. var renderState = appearance.getRenderState();
  833. var rs;
  834. if (twoPasses) {
  835. rs = clone(renderState, false);
  836. rs.cull = {
  837. enabled : true,
  838. face : CullFace.BACK
  839. };
  840. this._frontFaceRS = context.createRenderState(rs);
  841. rs.cull.face = CullFace.FRONT;
  842. this._backFaceRS = context.createRenderState(rs);
  843. } else {
  844. this._frontFaceRS = context.createRenderState(renderState);
  845. this._backFaceRS = this._frontFaceRS;
  846. }
  847. if (allowPicking) {
  848. if (twoPasses) {
  849. rs = clone(renderState, false);
  850. rs.cull = {
  851. enabled : false
  852. };
  853. this._pickRS = context.createRenderState(rs);
  854. } else {
  855. this._pickRS = this._frontFaceRS;
  856. }
  857. } else {
  858. rs = clone(renderState, false);
  859. rs.colorMask = {
  860. red : false,
  861. green : false,
  862. blue : false,
  863. alpha : false
  864. };
  865. if (twoPasses) {
  866. rs.cull = {
  867. enabled : false
  868. };
  869. this._pickRS = context.createRenderState(rs);
  870. } else {
  871. this._pickRS = context.createRenderState(rs);
  872. }
  873. }
  874. }
  875. if (createSP) {
  876. var vs = createColumbusViewShader(this, appearance.vertexShaderSource, scene3DOnly);
  877. vs = appendShow(this, vs);
  878. vs = modifyForEncodedNormals(this, vs);
  879. var fs = appearance.getFragmentShaderSource();
  880. this._sp = context.replaceShaderProgram(this._sp, vs, fs, attributeLocations);
  881. validateShaderMatching(this._sp, attributeLocations);
  882. if (allowPicking) {
  883. var pickFS = new ShaderSource({
  884. sources : [fs],
  885. pickColorQualifier : 'varying'
  886. });
  887. this._pickSP = context.replaceShaderProgram(this._pickSP, createPickVertexShaderSource(vs), pickFS, attributeLocations);
  888. } else {
  889. this._pickSP = context.createShaderProgram(vs, fs, attributeLocations);
  890. }
  891. validateShaderMatching(this._pickSP, attributeLocations);
  892. }
  893. var colorCommands = this._colorCommands;
  894. var pickCommands = this._pickCommands;
  895. if (createRS || createSP) {
  896. // Create uniform map by combining uniforms from the appearance and material if either have uniforms.
  897. var materialUniformMap = defined(material) ? material._uniforms : undefined;
  898. var appearanceUniformMap = {};
  899. var appearanceUniforms = appearance.uniforms;
  900. if (defined(appearanceUniforms)) {
  901. // Convert to uniform map of functions for the renderer
  902. for (var name in appearanceUniforms) {
  903. if (appearanceUniforms.hasOwnProperty(name)) {
  904. if (defined(materialUniformMap) && defined(materialUniformMap[name])) {
  905. // Later, we could rename uniforms behind-the-scenes if needed.
  906. throw new DeveloperError('Appearance and material have a uniform with the same name: ' + name);
  907. }
  908. appearanceUniformMap[name] = getUniformFunction(appearanceUniforms, name);
  909. }
  910. }
  911. }
  912. var uniforms = combine(appearanceUniformMap, materialUniformMap);
  913. var pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
  914. colorCommands.length = this._va.length * (twoPasses ? 2 : 1);
  915. pickCommands.length = this._va.length;
  916. length = colorCommands.length;
  917. var m = 0;
  918. var vaIndex = 0;
  919. for (i = 0; i < length; ++i) {
  920. if (twoPasses) {
  921. colorCommand = colorCommands[i];
  922. if (!defined(colorCommand)) {
  923. colorCommand = colorCommands[i] = new DrawCommand({
  924. owner : this,
  925. primitiveType : this._primitiveType
  926. });
  927. }
  928. colorCommand.vertexArray = this._va[vaIndex];
  929. colorCommand.renderState = this._backFaceRS;
  930. colorCommand.shaderProgram = this._sp;
  931. colorCommand.uniformMap = uniforms;
  932. colorCommand.pass = pass;
  933. ++i;
  934. }
  935. colorCommand = colorCommands[i];
  936. if (!defined(colorCommand)) {
  937. colorCommand = colorCommands[i] = new DrawCommand({
  938. owner : this,
  939. primitiveType : this._primitiveType
  940. });
  941. }
  942. colorCommand.vertexArray = this._va[vaIndex];
  943. colorCommand.renderState = this._frontFaceRS;
  944. colorCommand.shaderProgram = this._sp;
  945. colorCommand.uniformMap = uniforms;
  946. colorCommand.pass = pass;
  947. pickCommand = pickCommands[m];
  948. if (!defined(pickCommand)) {
  949. pickCommand = pickCommands[m] = new DrawCommand({
  950. owner : this,
  951. primitiveType : this._primitiveType
  952. });
  953. }
  954. pickCommand.vertexArray = this._va[vaIndex];
  955. pickCommand.renderState = this._pickRS;
  956. pickCommand.shaderProgram = this._pickSP;
  957. pickCommand.uniformMap = uniforms;
  958. pickCommand.pass = pass;
  959. ++m;
  960. ++vaIndex;
  961. }
  962. }
  963. // Update per-instance attributes
  964. if (this._dirtyAttributes.length > 0) {
  965. attributes = this._dirtyAttributes;
  966. length = attributes.length;
  967. for (i = 0; i < length; ++i) {
  968. attribute = attributes[i];
  969. var value = attribute.value;
  970. var indices = attribute.indices;
  971. var indicesLength = indices.length;
  972. for (j = 0; j < indicesLength; ++j) {
  973. index = indices[j];
  974. var offset = index.offset;
  975. var count = index.count;
  976. var vaAttribute = index.attribute;
  977. var componentDatatype = vaAttribute.componentDatatype;
  978. var componentsPerAttribute = vaAttribute.componentsPerAttribute;
  979. var typedArray = ComponentDatatype.createTypedArray(componentDatatype, count * componentsPerAttribute);
  980. for (var k = 0; k < count; ++k) {
  981. typedArray.set(value, k * componentsPerAttribute);
  982. }
  983. var offsetInBytes = offset * componentsPerAttribute * ComponentDatatype.getSizeInBytes(componentDatatype);
  984. vaAttribute.vertexBuffer.copyFromArrayView(typedArray, offsetInBytes);
  985. }
  986. attribute.dirty = false;
  987. }
  988. attributes.length = 0;
  989. }
  990. var modelMatrix;
  991. if ((this._numberOfInstances > 1 && !this._validModelMatrix) || frameState.mode !== SceneMode.SCENE3D) {
  992. modelMatrix = Matrix4.IDENTITY;
  993. } else {
  994. modelMatrix = this.modelMatrix;
  995. }
  996. if (!Matrix4.equals(modelMatrix, this._modelMatrix)) {
  997. Matrix4.clone(modelMatrix, this._modelMatrix);
  998. length = this._boundingSpheres.length;
  999. for (i = 0; i < length; ++i) {
  1000. var boundingSphere = this._boundingSpheres[i];
  1001. if (defined(boundingSphere)) {
  1002. this._boundingSphereWC[i] = BoundingSphere.transform(boundingSphere, modelMatrix, this._boundingSphereWC[i]);
  1003. if (!scene3DOnly) {
  1004. this._boundingSphere2D[i] = BoundingSphere.clone(this._boundingSphereCV[i], this._boundingSphere2D[i]);
  1005. this._boundingSphere2D[i].center.x = 0.0;
  1006. this._boundingSphereMorph[i] = BoundingSphere.union(this._boundingSphereWC[i], this._boundingSphereCV[i]);
  1007. }
  1008. }
  1009. }
  1010. }
  1011. var boundingSpheres;
  1012. if (frameState.mode === SceneMode.SCENE3D) {
  1013. boundingSpheres = this._boundingSphereWC;
  1014. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  1015. boundingSpheres = this._boundingSphereCV;
  1016. } else if (frameState.mode === SceneMode.SCENE2D && defined(this._boundingSphere2D)) {
  1017. boundingSpheres = this._boundingSphere2D;
  1018. } else if (defined(this._boundingSphereMorph)) {
  1019. boundingSpheres = this._boundingSphereMorph;
  1020. }
  1021. var passes = frameState.passes;
  1022. if (passes.render) {
  1023. length = colorCommands.length;
  1024. for (i = 0; i < length; ++i) {
  1025. var sphereIndex = twoPasses ? Math.floor(i / 2) : i;
  1026. colorCommands[i].modelMatrix = modelMatrix;
  1027. colorCommands[i].boundingVolume = boundingSpheres[sphereIndex];
  1028. colorCommands[i].cull = this.cull;
  1029. colorCommands[i].debugShowBoundingVolume = this.debugShowBoundingVolume;
  1030. commandList.push(colorCommands[i]);
  1031. }
  1032. }
  1033. if (passes.pick) {
  1034. length = pickCommands.length;
  1035. for (i = 0; i < length; ++i) {
  1036. pickCommands[i].modelMatrix = modelMatrix;
  1037. pickCommands[i].boundingVolume = boundingSpheres[i];
  1038. pickCommands[i].cull = this.cull;
  1039. commandList.push(pickCommands[i]);
  1040. }
  1041. }
  1042. };
  1043. function createGetFunction(name, perInstanceAttributes) {
  1044. return function() {
  1045. return perInstanceAttributes[name].value;
  1046. };
  1047. }
  1048. function createSetFunction(name, perInstanceAttributes, dirtyList) {
  1049. return function (value) {
  1050. //>>includeStart('debug', pragmas.debug);
  1051. if (!defined(value) || !defined(value.length) || value.length < 1 || value.length > 4) {
  1052. throw new DeveloperError('value must be and array with length between 1 and 4.');
  1053. }
  1054. //>>includeEnd('debug');
  1055. var attribute = perInstanceAttributes[name];
  1056. attribute.value = value;
  1057. if (!attribute.dirty) {
  1058. dirtyList.push(attribute);
  1059. attribute.dirty = true;
  1060. }
  1061. };
  1062. }
  1063. /**
  1064. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  1065. *
  1066. * @param {Object} id The id of the {@link GeometryInstance}.
  1067. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id.
  1068. *
  1069. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  1070. *
  1071. * @example
  1072. * var attributes = primitive.getGeometryInstanceAttributes('an id');
  1073. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  1074. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  1075. */
  1076. Primitive.prototype.getGeometryInstanceAttributes = function(id) {
  1077. //>>includeStart('debug', pragmas.debug);
  1078. if (!defined(id)) {
  1079. throw new DeveloperError('id is required');
  1080. }
  1081. if (!defined(this._perInstanceAttributeLocations)) {
  1082. throw new DeveloperError('must call update before calling getGeometryInstanceAttributes');
  1083. }
  1084. //>>includeEnd('debug');
  1085. var index = -1;
  1086. var lastIndex = this._lastPerInstanceAttributeIndex;
  1087. var ids = this._instanceIds;
  1088. var length = ids.length;
  1089. for (var i = 0; i < length; ++i) {
  1090. var curIndex = (lastIndex + i) % length;
  1091. if (id === ids[curIndex]) {
  1092. index = curIndex;
  1093. break;
  1094. }
  1095. }
  1096. if (index === -1) {
  1097. return undefined;
  1098. }
  1099. var perInstanceAttributes = this._perInstanceAttributeLocations[index];
  1100. var attributes = {};
  1101. var properties = {};
  1102. var hasProperties = false;
  1103. for (var name in perInstanceAttributes) {
  1104. if (perInstanceAttributes.hasOwnProperty(name)) {
  1105. hasProperties = true;
  1106. properties[name] = {
  1107. get : createGetFunction(name, perInstanceAttributes),
  1108. set : createSetFunction(name, perInstanceAttributes, this._dirtyAttributes)
  1109. };
  1110. }
  1111. }
  1112. if (hasProperties) {
  1113. defineProperties(attributes, properties);
  1114. }
  1115. this._lastPerInstanceAttributeIndex = index;
  1116. return attributes;
  1117. };
  1118. /**
  1119. * Returns true if this object was destroyed; otherwise, false.
  1120. * <p>
  1121. * If this object was destroyed, it should not be used; calling any function other than
  1122. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1123. * </p>
  1124. *
  1125. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1126. *
  1127. * @see Primitive#destroy
  1128. */
  1129. Primitive.prototype.isDestroyed = function() {
  1130. return false;
  1131. };
  1132. /**
  1133. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1134. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1135. * <p>
  1136. * Once an object is destroyed, it should not be used; calling any function other than
  1137. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1138. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1139. * </p>
  1140. *
  1141. * @returns {undefined}
  1142. *
  1143. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1144. *
  1145. * @see Primitive#isDestroyed
  1146. *
  1147. * @example
  1148. * e = e && e.destroy();
  1149. */
  1150. Primitive.prototype.destroy = function() {
  1151. var length;
  1152. var i;
  1153. this._sp = this._sp && this._sp.destroy();
  1154. this._pickSP = this._pickSP && this._pickSP.destroy();
  1155. var va = this._va;
  1156. length = va.length;
  1157. for (i = 0; i < length; ++i) {
  1158. va[i].destroy();
  1159. }
  1160. this._va = undefined;
  1161. var pickIds = this._pickIds;
  1162. length = pickIds.length;
  1163. for (i = 0; i < length; ++i) {
  1164. pickIds[i].destroy();
  1165. }
  1166. this._pickIds = undefined;
  1167. return destroyObject(this);
  1168. };
  1169. return Primitive;
  1170. });