PointCloud.js 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363
  1. import arraySlice from '../Core/arraySlice.js';
  2. import BoundingSphere from '../Core/BoundingSphere.js';
  3. import Cartesian3 from '../Core/Cartesian3.js';
  4. import Cartesian4 from '../Core/Cartesian4.js';
  5. import Check from '../Core/Check.js';
  6. import Color from '../Core/Color.js';
  7. import combine from '../Core/combine.js';
  8. import ComponentDatatype from '../Core/ComponentDatatype.js';
  9. import defaultValue from '../Core/defaultValue.js';
  10. import defined from '../Core/defined.js';
  11. import defineProperties from '../Core/defineProperties.js';
  12. import destroyObject from '../Core/destroyObject.js';
  13. import getStringFromTypedArray from '../Core/getStringFromTypedArray.js';
  14. import CesiumMath from '../Core/Math.js';
  15. import Matrix4 from '../Core/Matrix4.js';
  16. import oneTimeWarning from '../Core/oneTimeWarning.js';
  17. import OrthographicFrustum from '../Core/OrthographicFrustum.js';
  18. import PrimitiveType from '../Core/PrimitiveType.js';
  19. import RuntimeError from '../Core/RuntimeError.js';
  20. import Transforms from '../Core/Transforms.js';
  21. import Buffer from '../Renderer/Buffer.js';
  22. import BufferUsage from '../Renderer/BufferUsage.js';
  23. import DrawCommand from '../Renderer/DrawCommand.js';
  24. import Pass from '../Renderer/Pass.js';
  25. import RenderState from '../Renderer/RenderState.js';
  26. import ShaderProgram from '../Renderer/ShaderProgram.js';
  27. import VertexArray from '../Renderer/VertexArray.js';
  28. import when from '../ThirdParty/when.js';
  29. import BlendingState from './BlendingState.js';
  30. import Cesium3DTileBatchTable from './Cesium3DTileBatchTable.js';
  31. import Cesium3DTileFeatureTable from './Cesium3DTileFeatureTable.js';
  32. import DracoLoader from './DracoLoader.js';
  33. import getClipAndStyleCode from './getClipAndStyleCode.js';
  34. import getClippingFunction from './getClippingFunction.js';
  35. import SceneMode from './SceneMode.js';
  36. import ShadowMode from './ShadowMode.js';
  37. import StencilConstants from './StencilConstants.js';
  38. var DecodingState = {
  39. NEEDS_DECODE : 0,
  40. DECODING : 1,
  41. READY : 2,
  42. FAILED : 3
  43. };
  44. /**
  45. * Represents the contents of a
  46. * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification/TileFormats/PointCloud|Point Cloud}
  47. * tile. Used internally by {@link PointCloud3DTileContent} and {@link TimeDynamicPointCloud}.
  48. *
  49. * @alias PointCloud
  50. * @constructor
  51. *
  52. * @see PointCloud3DTileContent
  53. * @see TimeDynamicPointCloud
  54. *
  55. * @private
  56. */
  57. function PointCloud(options) {
  58. //>>includeStart('debug', pragmas.debug);
  59. Check.typeOf.object('options', options);
  60. Check.typeOf.object('options.arrayBuffer', options.arrayBuffer);
  61. //>>includeEnd('debug');
  62. // Hold onto the payload until the render resources are created
  63. this._parsedContent = undefined;
  64. this._drawCommand = undefined;
  65. this._isTranslucent = false;
  66. this._styleTranslucent = false;
  67. this._constantColor = Color.clone(Color.DARKGRAY);
  68. this._highlightColor = Color.clone(Color.WHITE);
  69. this._pointSize = 1.0;
  70. this._rtcCenter = undefined;
  71. this._quantizedVolumeScale = undefined;
  72. this._quantizedVolumeOffset = undefined;
  73. // These values are used to regenerate the shader when the style changes
  74. this._styleableShaderAttributes = undefined;
  75. this._isQuantized = false;
  76. this._isOctEncoded16P = false;
  77. this._isRGB565 = false;
  78. this._hasColors = false;
  79. this._hasNormals = false;
  80. this._hasBatchIds = false;
  81. // Draco
  82. this._decodingState = DecodingState.READY;
  83. this._dequantizeInShader = true;
  84. this._isQuantizedDraco = false;
  85. this._isOctEncodedDraco = false;
  86. this._quantizedRange = 0.0;
  87. this._octEncodedRange = 0.0;
  88. // Use per-point normals to hide back-facing points.
  89. this.backFaceCulling = false;
  90. this._backFaceCulling = false;
  91. // Whether to enable normal shading
  92. this.normalShading = true;
  93. this._normalShading = true;
  94. this._opaqueRenderState = undefined;
  95. this._translucentRenderState = undefined;
  96. this._mode = undefined;
  97. this._ready = false;
  98. this._readyPromise = when.defer();
  99. this._pointsLength = 0;
  100. this._geometryByteLength = 0;
  101. this._vertexShaderLoaded = options.vertexShaderLoaded;
  102. this._fragmentShaderLoaded = options.fragmentShaderLoaded;
  103. this._uniformMapLoaded = options.uniformMapLoaded;
  104. this._batchTableLoaded = options.batchTableLoaded;
  105. this._pickIdLoaded = options.pickIdLoaded;
  106. this._opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
  107. this._cull = defaultValue(options.cull, true);
  108. this.style = undefined;
  109. this._style = undefined;
  110. this.styleDirty = false;
  111. this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  112. this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  113. this.time = 0.0; // For styling
  114. this.shadows = ShadowMode.ENABLED;
  115. this._boundingSphere = undefined;
  116. this.clippingPlanes = undefined;
  117. this.isClipped = false;
  118. this.clippingPlanesDirty = false;
  119. // If defined, use this matrix to position the clipping planes instead of the modelMatrix.
  120. // This is so that when point clouds are part of a tileset they all get clipped relative
  121. // to the root tile.
  122. this.clippingPlanesOriginMatrix = undefined;
  123. this.attenuation = false;
  124. this._attenuation = false;
  125. // Options for geometric error based attenuation
  126. this.geometricError = 0.0;
  127. this.geometricErrorScale = 1.0;
  128. this.maximumAttenuation = this._pointSize;
  129. initialize(this, options);
  130. }
  131. defineProperties(PointCloud.prototype, {
  132. pointsLength : {
  133. get : function() {
  134. return this._pointsLength;
  135. }
  136. },
  137. geometryByteLength : {
  138. get : function() {
  139. return this._geometryByteLength;
  140. }
  141. },
  142. ready : {
  143. get : function() {
  144. return this._ready;
  145. }
  146. },
  147. readyPromise : {
  148. get : function() {
  149. return this._readyPromise.promise;
  150. }
  151. },
  152. color : {
  153. get : function() {
  154. return Color.clone(this._highlightColor);
  155. },
  156. set : function(value) {
  157. this._highlightColor = Color.clone(value, this._highlightColor);
  158. }
  159. },
  160. boundingSphere : {
  161. get : function() {
  162. if (defined(this._drawCommand)) {
  163. return this._drawCommand.boundingVolume;
  164. }
  165. },
  166. set : function(value) {
  167. this._boundingSphere = BoundingSphere.clone(value);
  168. }
  169. }
  170. });
  171. var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  172. function initialize(pointCloud, options) {
  173. var arrayBuffer = options.arrayBuffer;
  174. var byteOffset = defaultValue(options.byteOffset, 0);
  175. var uint8Array = new Uint8Array(arrayBuffer);
  176. var view = new DataView(arrayBuffer);
  177. byteOffset += sizeOfUint32; // Skip magic
  178. var version = view.getUint32(byteOffset, true);
  179. if (version !== 1) {
  180. throw new RuntimeError('Only Point Cloud tile version 1 is supported. Version ' + version + ' is not.');
  181. }
  182. byteOffset += sizeOfUint32;
  183. // Skip byteLength
  184. byteOffset += sizeOfUint32;
  185. var featureTableJsonByteLength = view.getUint32(byteOffset, true);
  186. if (featureTableJsonByteLength === 0) {
  187. throw new RuntimeError('Feature table must have a byte length greater than zero');
  188. }
  189. byteOffset += sizeOfUint32;
  190. var featureTableBinaryByteLength = view.getUint32(byteOffset, true);
  191. byteOffset += sizeOfUint32;
  192. var batchTableJsonByteLength = view.getUint32(byteOffset, true);
  193. byteOffset += sizeOfUint32;
  194. var batchTableBinaryByteLength = view.getUint32(byteOffset, true);
  195. byteOffset += sizeOfUint32;
  196. var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength);
  197. var featureTableJson = JSON.parse(featureTableString);
  198. byteOffset += featureTableJsonByteLength;
  199. var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength);
  200. byteOffset += featureTableBinaryByteLength;
  201. // Get the batch table JSON and binary
  202. var batchTableJson;
  203. var batchTableBinary;
  204. if (batchTableJsonByteLength > 0) {
  205. // Has a batch table JSON
  206. var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJsonByteLength);
  207. batchTableJson = JSON.parse(batchTableString);
  208. byteOffset += batchTableJsonByteLength;
  209. if (batchTableBinaryByteLength > 0) {
  210. // Has a batch table binary
  211. batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength);
  212. byteOffset += batchTableBinaryByteLength;
  213. }
  214. }
  215. var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary);
  216. var pointsLength = featureTable.getGlobalProperty('POINTS_LENGTH');
  217. featureTable.featuresLength = pointsLength;
  218. if (!defined(pointsLength)) {
  219. throw new RuntimeError('Feature table global property: POINTS_LENGTH must be defined');
  220. }
  221. var rtcCenter = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3);
  222. if (defined(rtcCenter)) {
  223. pointCloud._rtcCenter = Cartesian3.unpack(rtcCenter);
  224. }
  225. var positions;
  226. var colors;
  227. var normals;
  228. var batchIds;
  229. var hasPositions = false;
  230. var hasColors = false;
  231. var hasNormals = false;
  232. var hasBatchIds = false;
  233. var isQuantized = false;
  234. var isTranslucent = false;
  235. var isRGB565 = false;
  236. var isOctEncoded16P = false;
  237. var dracoBuffer;
  238. var dracoFeatureTableProperties;
  239. var dracoBatchTableProperties;
  240. var featureTableDraco = defined(featureTableJson.extensions) ? featureTableJson.extensions['3DTILES_draco_point_compression'] : undefined;
  241. var batchTableDraco = (defined(batchTableJson) && defined(batchTableJson.extensions)) ? batchTableJson.extensions['3DTILES_draco_point_compression'] : undefined;
  242. if (defined(batchTableDraco)) {
  243. dracoBatchTableProperties = batchTableDraco.properties;
  244. }
  245. if (defined(featureTableDraco)) {
  246. dracoFeatureTableProperties = featureTableDraco.properties;
  247. var dracoByteOffset = featureTableDraco.byteOffset;
  248. var dracoByteLength = featureTableDraco.byteLength;
  249. if (!defined(dracoFeatureTableProperties) || !defined(dracoByteOffset) || !defined(dracoByteLength)) {
  250. throw new RuntimeError('Draco properties, byteOffset, and byteLength must be defined');
  251. }
  252. dracoBuffer = arraySlice(featureTableBinary, dracoByteOffset, dracoByteOffset + dracoByteLength);
  253. hasPositions = defined(dracoFeatureTableProperties.POSITION);
  254. hasColors = defined(dracoFeatureTableProperties.RGB) || defined(dracoFeatureTableProperties.RGBA);
  255. hasNormals = defined(dracoFeatureTableProperties.NORMAL);
  256. hasBatchIds = defined(dracoFeatureTableProperties.BATCH_ID);
  257. isTranslucent = defined(dracoFeatureTableProperties.RGBA);
  258. pointCloud._decodingState = DecodingState.NEEDS_DECODE;
  259. }
  260. var draco;
  261. if (defined(dracoBuffer)) {
  262. draco = {
  263. buffer : dracoBuffer,
  264. featureTableProperties : dracoFeatureTableProperties,
  265. batchTableProperties : dracoBatchTableProperties,
  266. properties : combine(dracoFeatureTableProperties, dracoBatchTableProperties),
  267. dequantizeInShader : pointCloud._dequantizeInShader
  268. };
  269. }
  270. if (!hasPositions) {
  271. if (defined(featureTableJson.POSITION)) {
  272. positions = featureTable.getPropertyArray('POSITION', ComponentDatatype.FLOAT, 3);
  273. hasPositions = true;
  274. } else if (defined(featureTableJson.POSITION_QUANTIZED)) {
  275. positions = featureTable.getPropertyArray('POSITION_QUANTIZED', ComponentDatatype.UNSIGNED_SHORT, 3);
  276. isQuantized = true;
  277. hasPositions = true;
  278. var quantizedVolumeScale = featureTable.getGlobalProperty('QUANTIZED_VOLUME_SCALE', ComponentDatatype.FLOAT, 3);
  279. if (!defined(quantizedVolumeScale)) {
  280. throw new RuntimeError('Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions.');
  281. }
  282. pointCloud._quantizedVolumeScale = Cartesian3.unpack(quantizedVolumeScale);
  283. pointCloud._quantizedRange = (1 << 16) - 1;
  284. var quantizedVolumeOffset = featureTable.getGlobalProperty('QUANTIZED_VOLUME_OFFSET', ComponentDatatype.FLOAT, 3);
  285. if (!defined(quantizedVolumeOffset)) {
  286. throw new RuntimeError('Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions.');
  287. }
  288. pointCloud._quantizedVolumeOffset = Cartesian3.unpack(quantizedVolumeOffset);
  289. }
  290. }
  291. if (!hasColors) {
  292. if (defined(featureTableJson.RGBA)) {
  293. colors = featureTable.getPropertyArray('RGBA', ComponentDatatype.UNSIGNED_BYTE, 4);
  294. isTranslucent = true;
  295. hasColors = true;
  296. } else if (defined(featureTableJson.RGB)) {
  297. colors = featureTable.getPropertyArray('RGB', ComponentDatatype.UNSIGNED_BYTE, 3);
  298. hasColors = true;
  299. } else if (defined(featureTableJson.RGB565)) {
  300. colors = featureTable.getPropertyArray('RGB565', ComponentDatatype.UNSIGNED_SHORT, 1);
  301. isRGB565 = true;
  302. hasColors = true;
  303. }
  304. }
  305. if (!hasNormals) {
  306. if (defined(featureTableJson.NORMAL)) {
  307. normals = featureTable.getPropertyArray('NORMAL', ComponentDatatype.FLOAT, 3);
  308. hasNormals = true;
  309. } else if (defined(featureTableJson.NORMAL_OCT16P)) {
  310. normals = featureTable.getPropertyArray('NORMAL_OCT16P', ComponentDatatype.UNSIGNED_BYTE, 2);
  311. isOctEncoded16P = true;
  312. hasNormals = true;
  313. }
  314. }
  315. if (!hasBatchIds) {
  316. if (defined(featureTableJson.BATCH_ID)) {
  317. batchIds = featureTable.getPropertyArray('BATCH_ID', ComponentDatatype.UNSIGNED_SHORT, 1);
  318. hasBatchIds = true;
  319. }
  320. }
  321. if (!hasPositions) {
  322. throw new RuntimeError('Either POSITION or POSITION_QUANTIZED must be defined.');
  323. }
  324. if (defined(featureTableJson.CONSTANT_RGBA)) {
  325. var constantRGBA = featureTable.getGlobalProperty('CONSTANT_RGBA', ComponentDatatype.UNSIGNED_BYTE, 4);
  326. pointCloud._constantColor = Color.fromBytes(constantRGBA[0], constantRGBA[1], constantRGBA[2], constantRGBA[3], pointCloud._constantColor);
  327. }
  328. if (hasBatchIds) {
  329. var batchLength = featureTable.getGlobalProperty('BATCH_LENGTH');
  330. if (!defined(batchLength)) {
  331. throw new RuntimeError('Global property: BATCH_LENGTH must be defined when BATCH_ID is defined.');
  332. }
  333. if (defined(batchTableBinary)) {
  334. // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
  335. batchTableBinary = new Uint8Array(batchTableBinary);
  336. }
  337. if (defined(pointCloud._batchTableLoaded)) {
  338. pointCloud._batchTableLoaded(batchLength, batchTableJson, batchTableBinary);
  339. }
  340. }
  341. // If points are not batched and there are per-point properties, use these properties for styling purposes
  342. var styleableProperties;
  343. if (!hasBatchIds && defined(batchTableBinary)) {
  344. styleableProperties = Cesium3DTileBatchTable.getBinaryProperties(pointsLength, batchTableJson, batchTableBinary);
  345. }
  346. pointCloud._parsedContent = {
  347. positions : positions,
  348. colors : colors,
  349. normals : normals,
  350. batchIds : batchIds,
  351. styleableProperties : styleableProperties,
  352. draco : draco
  353. };
  354. pointCloud._pointsLength = pointsLength;
  355. pointCloud._isQuantized = isQuantized;
  356. pointCloud._isOctEncoded16P = isOctEncoded16P;
  357. pointCloud._isRGB565 = isRGB565;
  358. pointCloud._isTranslucent = isTranslucent;
  359. pointCloud._hasColors = hasColors;
  360. pointCloud._hasNormals = hasNormals;
  361. pointCloud._hasBatchIds = hasBatchIds;
  362. }
  363. var scratchMin = new Cartesian3();
  364. var scratchMax = new Cartesian3();
  365. var scratchPosition = new Cartesian3();
  366. var randomValues;
  367. function getRandomValues(samplesLength) {
  368. // Use same random values across all runs
  369. if (!defined(randomValues)) {
  370. CesiumMath.setRandomNumberSeed(0);
  371. randomValues = new Array(samplesLength);
  372. for (var i = 0; i < samplesLength; ++i) {
  373. randomValues[i] = CesiumMath.nextRandomNumber();
  374. }
  375. }
  376. return randomValues;
  377. }
  378. function computeApproximateBoundingSphereFromPositions(positions) {
  379. var maximumSamplesLength = 20;
  380. var pointsLength = positions.length / 3;
  381. var samplesLength = Math.min(pointsLength, maximumSamplesLength);
  382. var randomValues = getRandomValues(maximumSamplesLength);
  383. var maxValue = Number.MAX_VALUE;
  384. var minValue = -Number.MAX_VALUE;
  385. var min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin);
  386. var max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax);
  387. for (var i = 0; i < samplesLength; ++i) {
  388. var index = Math.floor(randomValues[i] * pointsLength);
  389. var position = Cartesian3.unpack(positions, index * 3, scratchPosition);
  390. Cartesian3.minimumByComponent(min, position, min);
  391. Cartesian3.maximumByComponent(max, position, max);
  392. }
  393. var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
  394. boundingSphere.radius += CesiumMath.EPSILON2; // To avoid radius of zero
  395. return boundingSphere;
  396. }
  397. function prepareVertexAttribute(typedArray, name) {
  398. // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT.
  399. var componentDatatype = ComponentDatatype.fromTypedArray(typedArray);
  400. if (componentDatatype === ComponentDatatype.INT || componentDatatype === ComponentDatatype.UNSIGNED_INT || componentDatatype === ComponentDatatype.DOUBLE) {
  401. oneTimeWarning('Cast pnts property to floats', 'Point cloud property "' + name + '" will be casted to a float array because INT, UNSIGNED_INT, and DOUBLE are not valid WebGL vertex attribute types. Some precision may be lost.');
  402. return new Float32Array(typedArray);
  403. }
  404. return typedArray;
  405. }
  406. var scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier = new Cartesian4();
  407. var scratchQuantizedVolumeScaleAndOctEncodedRange = new Cartesian4();
  408. var scratchColor = new Color();
  409. var positionLocation = 0;
  410. var colorLocation = 1;
  411. var normalLocation = 2;
  412. var batchIdLocation = 3;
  413. var numberOfAttributes = 4;
  414. var scratchClippingPlaneMatrix = new Matrix4();
  415. function createResources(pointCloud, frameState) {
  416. var context = frameState.context;
  417. var parsedContent = pointCloud._parsedContent;
  418. var pointsLength = pointCloud._pointsLength;
  419. var positions = parsedContent.positions;
  420. var colors = parsedContent.colors;
  421. var normals = parsedContent.normals;
  422. var batchIds = parsedContent.batchIds;
  423. var styleableProperties = parsedContent.styleableProperties;
  424. var hasStyleableProperties = defined(styleableProperties);
  425. var isQuantized = pointCloud._isQuantized;
  426. var isQuantizedDraco = pointCloud._isQuantizedDraco;
  427. var isOctEncoded16P = pointCloud._isOctEncoded16P;
  428. var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  429. var quantizedRange = pointCloud._quantizedRange;
  430. var octEncodedRange = pointCloud._octEncodedRange;
  431. var isRGB565 = pointCloud._isRGB565;
  432. var isTranslucent = pointCloud._isTranslucent;
  433. var hasColors = pointCloud._hasColors;
  434. var hasNormals = pointCloud._hasNormals;
  435. var hasBatchIds = pointCloud._hasBatchIds;
  436. var componentsPerAttribute;
  437. var componentDatatype;
  438. var styleableVertexAttributes = [];
  439. var styleableShaderAttributes = {};
  440. pointCloud._styleableShaderAttributes = styleableShaderAttributes;
  441. if (hasStyleableProperties) {
  442. var attributeLocation = numberOfAttributes;
  443. for (var name in styleableProperties) {
  444. if (styleableProperties.hasOwnProperty(name)) {
  445. var property = styleableProperties[name];
  446. var typedArray = prepareVertexAttribute(property.typedArray, name);
  447. componentsPerAttribute = property.componentCount;
  448. componentDatatype = ComponentDatatype.fromTypedArray(typedArray);
  449. var vertexBuffer = Buffer.createVertexBuffer({
  450. context : context,
  451. typedArray : typedArray,
  452. usage : BufferUsage.STATIC_DRAW
  453. });
  454. pointCloud._geometryByteLength += vertexBuffer.sizeInBytes;
  455. var vertexAttribute = {
  456. index : attributeLocation,
  457. vertexBuffer : vertexBuffer,
  458. componentsPerAttribute : componentsPerAttribute,
  459. componentDatatype : componentDatatype,
  460. normalize : false,
  461. offsetInBytes : 0,
  462. strideInBytes : 0
  463. };
  464. styleableVertexAttributes.push(vertexAttribute);
  465. styleableShaderAttributes[name] = {
  466. location : attributeLocation,
  467. componentCount : componentsPerAttribute
  468. };
  469. ++attributeLocation;
  470. }
  471. }
  472. }
  473. var positionsVertexBuffer = Buffer.createVertexBuffer({
  474. context : context,
  475. typedArray : positions,
  476. usage : BufferUsage.STATIC_DRAW
  477. });
  478. pointCloud._geometryByteLength += positionsVertexBuffer.sizeInBytes;
  479. var colorsVertexBuffer;
  480. if (hasColors) {
  481. colorsVertexBuffer = Buffer.createVertexBuffer({
  482. context : context,
  483. typedArray : colors,
  484. usage : BufferUsage.STATIC_DRAW
  485. });
  486. pointCloud._geometryByteLength += colorsVertexBuffer.sizeInBytes;
  487. }
  488. var normalsVertexBuffer;
  489. if (hasNormals) {
  490. normalsVertexBuffer = Buffer.createVertexBuffer({
  491. context : context,
  492. typedArray : normals,
  493. usage : BufferUsage.STATIC_DRAW
  494. });
  495. pointCloud._geometryByteLength += normalsVertexBuffer.sizeInBytes;
  496. }
  497. var batchIdsVertexBuffer;
  498. if (hasBatchIds) {
  499. batchIds = prepareVertexAttribute(batchIds, 'batchIds');
  500. batchIdsVertexBuffer = Buffer.createVertexBuffer({
  501. context : context,
  502. typedArray : batchIds,
  503. usage : BufferUsage.STATIC_DRAW
  504. });
  505. pointCloud._geometryByteLength += batchIdsVertexBuffer.sizeInBytes;
  506. }
  507. var attributes = [];
  508. if (isQuantized) {
  509. componentDatatype = ComponentDatatype.UNSIGNED_SHORT;
  510. } else if (isQuantizedDraco) {
  511. componentDatatype = (quantizedRange <= 255) ? ComponentDatatype.UNSIGNED_BYTE : ComponentDatatype.UNSIGNED_SHORT;
  512. } else {
  513. componentDatatype = ComponentDatatype.FLOAT;
  514. }
  515. attributes.push({
  516. index : positionLocation,
  517. vertexBuffer : positionsVertexBuffer,
  518. componentsPerAttribute : 3,
  519. componentDatatype : componentDatatype,
  520. normalize : false,
  521. offsetInBytes : 0,
  522. strideInBytes : 0
  523. });
  524. if (pointCloud._cull) {
  525. if (isQuantized || isQuantizedDraco) {
  526. pointCloud._boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.ZERO, pointCloud._quantizedVolumeScale);
  527. } else {
  528. pointCloud._boundingSphere = computeApproximateBoundingSphereFromPositions(positions);
  529. }
  530. }
  531. if (hasColors) {
  532. if (isRGB565) {
  533. attributes.push({
  534. index : colorLocation,
  535. vertexBuffer : colorsVertexBuffer,
  536. componentsPerAttribute : 1,
  537. componentDatatype : ComponentDatatype.UNSIGNED_SHORT,
  538. normalize : false,
  539. offsetInBytes : 0,
  540. strideInBytes : 0
  541. });
  542. } else {
  543. var colorComponentsPerAttribute = isTranslucent ? 4 : 3;
  544. attributes.push({
  545. index : colorLocation,
  546. vertexBuffer : colorsVertexBuffer,
  547. componentsPerAttribute : colorComponentsPerAttribute,
  548. componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  549. normalize : true,
  550. offsetInBytes : 0,
  551. strideInBytes : 0
  552. });
  553. }
  554. }
  555. if (hasNormals) {
  556. if (isOctEncoded16P) {
  557. componentsPerAttribute = 2;
  558. componentDatatype = ComponentDatatype.UNSIGNED_BYTE;
  559. } else if (isOctEncodedDraco) {
  560. componentsPerAttribute = 2;
  561. componentDatatype = (octEncodedRange <= 255) ? ComponentDatatype.UNSIGNED_BYTE : ComponentDatatype.UNSIGNED_SHORT;
  562. } else {
  563. componentsPerAttribute = 3;
  564. componentDatatype = ComponentDatatype.FLOAT;
  565. }
  566. attributes.push({
  567. index : normalLocation,
  568. vertexBuffer : normalsVertexBuffer,
  569. componentsPerAttribute : componentsPerAttribute,
  570. componentDatatype : componentDatatype,
  571. normalize : false,
  572. offsetInBytes : 0,
  573. strideInBytes : 0
  574. });
  575. }
  576. if (hasBatchIds) {
  577. attributes.push({
  578. index : batchIdLocation,
  579. vertexBuffer : batchIdsVertexBuffer,
  580. componentsPerAttribute : 1,
  581. componentDatatype : ComponentDatatype.fromTypedArray(batchIds),
  582. normalize : false,
  583. offsetInBytes : 0,
  584. strideInBytes : 0
  585. });
  586. }
  587. if (hasStyleableProperties) {
  588. attributes = attributes.concat(styleableVertexAttributes);
  589. }
  590. var vertexArray = new VertexArray({
  591. context : context,
  592. attributes : attributes
  593. });
  594. var opaqueRenderState = {
  595. depthTest : {
  596. enabled : true
  597. }
  598. };
  599. if (pointCloud._opaquePass === Pass.CESIUM_3D_TILE) {
  600. opaqueRenderState.stencilTest = StencilConstants.setCesium3DTileBit();
  601. opaqueRenderState.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
  602. }
  603. pointCloud._opaqueRenderState = RenderState.fromCache(opaqueRenderState);
  604. pointCloud._translucentRenderState = RenderState.fromCache({
  605. depthTest : {
  606. enabled : true
  607. },
  608. depthMask : false,
  609. blending : BlendingState.ALPHA_BLEND
  610. });
  611. pointCloud._drawCommand = new DrawCommand({
  612. boundingVolume : new BoundingSphere(),
  613. cull : pointCloud._cull,
  614. modelMatrix : new Matrix4(),
  615. primitiveType : PrimitiveType.POINTS,
  616. vertexArray : vertexArray,
  617. count : pointsLength,
  618. shaderProgram : undefined, // Updated in createShaders
  619. uniformMap : undefined, // Updated in createShaders
  620. renderState : isTranslucent ? pointCloud._translucentRenderState : pointCloud._opaqueRenderState,
  621. pass : isTranslucent ? Pass.TRANSLUCENT : pointCloud._opaquePass,
  622. owner : pointCloud,
  623. castShadows : false,
  624. receiveShadows : false,
  625. pickId : pointCloud._pickIdLoaded()
  626. });
  627. }
  628. function createUniformMap(pointCloud, frameState) {
  629. var context = frameState.context;
  630. var isQuantized = pointCloud._isQuantized;
  631. var isQuantizedDraco = pointCloud._isQuantizedDraco;
  632. var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  633. var uniformMap = {
  634. u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier : function() {
  635. var scratch = scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier;
  636. scratch.x = pointCloud._attenuation ? pointCloud.maximumAttenuation : pointCloud._pointSize;
  637. scratch.x *= frameState.pixelRatio;
  638. scratch.y = pointCloud.time;
  639. if (pointCloud._attenuation) {
  640. var frustum = frameState.camera.frustum;
  641. var depthMultiplier;
  642. // Attenuation is maximumAttenuation in 2D/ortho
  643. if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) {
  644. depthMultiplier = Number.POSITIVE_INFINITY;
  645. } else {
  646. depthMultiplier = context.drawingBufferHeight / frameState.camera.frustum.sseDenominator;
  647. }
  648. scratch.z = pointCloud.geometricError * pointCloud.geometricErrorScale;
  649. scratch.w = depthMultiplier;
  650. }
  651. return scratch;
  652. },
  653. u_highlightColor : function() {
  654. return pointCloud._highlightColor;
  655. },
  656. u_constantColor : function() {
  657. return pointCloud._constantColor;
  658. },
  659. u_clippingPlanes : function() {
  660. var clippingPlanes = pointCloud.clippingPlanes;
  661. var isClipped = pointCloud.isClipped;
  662. return isClipped ? clippingPlanes.texture : context.defaultTexture;
  663. },
  664. u_clippingPlanesEdgeStyle : function() {
  665. var clippingPlanes = pointCloud.clippingPlanes;
  666. if (!defined(clippingPlanes)) {
  667. return Color.TRANSPARENT;
  668. }
  669. var style = Color.clone(clippingPlanes.edgeColor, scratchColor);
  670. style.alpha = clippingPlanes.edgeWidth;
  671. return style;
  672. },
  673. u_clippingPlanesMatrix : function() {
  674. var clippingPlanes = pointCloud.clippingPlanes;
  675. if (!defined(clippingPlanes)) {
  676. return Matrix4.IDENTITY;
  677. }
  678. var clippingPlanesOriginMatrix = defaultValue(pointCloud.clippingPlanesOriginMatrix, pointCloud._modelMatrix);
  679. Matrix4.multiply(context.uniformState.view3D, clippingPlanesOriginMatrix, scratchClippingPlaneMatrix);
  680. return Matrix4.multiply(scratchClippingPlaneMatrix, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix);
  681. }
  682. };
  683. if (isQuantized || isQuantizedDraco || isOctEncodedDraco) {
  684. uniformMap = combine(uniformMap, {
  685. u_quantizedVolumeScaleAndOctEncodedRange : function() {
  686. var scratch = scratchQuantizedVolumeScaleAndOctEncodedRange;
  687. if (defined(pointCloud._quantizedVolumeScale)) {
  688. var scale = Cartesian3.clone(pointCloud._quantizedVolumeScale, scratch);
  689. Cartesian3.divideByScalar(scale, pointCloud._quantizedRange, scratch);
  690. }
  691. scratch.w = pointCloud._octEncodedRange;
  692. return scratch;
  693. }
  694. });
  695. }
  696. if (defined(pointCloud._uniformMapLoaded)) {
  697. uniformMap = pointCloud._uniformMapLoaded(uniformMap);
  698. }
  699. pointCloud._drawCommand.uniformMap = uniformMap;
  700. }
  701. var defaultProperties = ['POSITION', 'COLOR', 'NORMAL', 'POSITION_ABSOLUTE'];
  702. function getStyleableProperties(source, properties) {
  703. // Get all the properties used by this style
  704. var regex = /czm_tiles3d_style_(\w+)/g;
  705. var matches = regex.exec(source);
  706. while (matches !== null) {
  707. var name = matches[1];
  708. if (properties.indexOf(name) === -1) {
  709. properties.push(name);
  710. }
  711. matches = regex.exec(source);
  712. }
  713. }
  714. function getVertexAttribute(vertexArray, index) {
  715. var numberOfAttributes = vertexArray.numberOfAttributes;
  716. for (var i = 0; i < numberOfAttributes; ++i) {
  717. var attribute = vertexArray.getAttribute(i);
  718. if (attribute.index === index) {
  719. return attribute;
  720. }
  721. }
  722. }
  723. function modifyStyleFunction(source) {
  724. // Replace occurrences of czm_tiles3d_style_DEFAULTPROPERTY
  725. var length = defaultProperties.length;
  726. for (var i = 0; i < length; ++i) {
  727. var property = defaultProperties[i];
  728. var styleName = 'czm_tiles3d_style_' + property;
  729. var replaceName = property.toLowerCase();
  730. source = source.replace(new RegExp(styleName + '(\\W)', 'g'), replaceName + '$1');
  731. }
  732. // Edit the function header to accept the point position, color, and normal
  733. return source.replace('()', '(vec3 position, vec3 position_absolute, vec4 color, vec3 normal)');
  734. }
  735. function createShaders(pointCloud, frameState, style) {
  736. var i;
  737. var name;
  738. var attribute;
  739. var context = frameState.context;
  740. var hasStyle = defined(style);
  741. var isQuantized = pointCloud._isQuantized;
  742. var isQuantizedDraco = pointCloud._isQuantizedDraco;
  743. var isOctEncoded16P = pointCloud._isOctEncoded16P;
  744. var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  745. var isRGB565 = pointCloud._isRGB565;
  746. var isTranslucent = pointCloud._isTranslucent;
  747. var hasColors = pointCloud._hasColors;
  748. var hasNormals = pointCloud._hasNormals;
  749. var hasBatchIds = pointCloud._hasBatchIds;
  750. var backFaceCulling = pointCloud._backFaceCulling;
  751. var normalShading = pointCloud._normalShading;
  752. var vertexArray = pointCloud._drawCommand.vertexArray;
  753. var clippingPlanes = pointCloud.clippingPlanes;
  754. var attenuation = pointCloud._attenuation;
  755. var colorStyleFunction;
  756. var showStyleFunction;
  757. var pointSizeStyleFunction;
  758. var styleTranslucent = isTranslucent;
  759. if (hasStyle) {
  760. var shaderState = {
  761. translucent : false
  762. };
  763. colorStyleFunction = style.getColorShaderFunction('getColorFromStyle', 'czm_tiles3d_style_', shaderState);
  764. showStyleFunction = style.getShowShaderFunction('getShowFromStyle', 'czm_tiles3d_style_', shaderState);
  765. pointSizeStyleFunction = style.getPointSizeShaderFunction('getPointSizeFromStyle', 'czm_tiles3d_style_', shaderState);
  766. if (defined(colorStyleFunction) && shaderState.translucent) {
  767. styleTranslucent = true;
  768. }
  769. }
  770. pointCloud._styleTranslucent = styleTranslucent;
  771. var hasColorStyle = defined(colorStyleFunction);
  772. var hasShowStyle = defined(showStyleFunction);
  773. var hasPointSizeStyle = defined(pointSizeStyleFunction);
  774. var hasClippedContent = pointCloud.isClipped;
  775. // Get the properties in use by the style
  776. var styleableProperties = [];
  777. if (hasColorStyle) {
  778. getStyleableProperties(colorStyleFunction, styleableProperties);
  779. colorStyleFunction = modifyStyleFunction(colorStyleFunction);
  780. }
  781. if (hasShowStyle) {
  782. getStyleableProperties(showStyleFunction, styleableProperties);
  783. showStyleFunction = modifyStyleFunction(showStyleFunction);
  784. }
  785. if (hasPointSizeStyle) {
  786. getStyleableProperties(pointSizeStyleFunction, styleableProperties);
  787. pointSizeStyleFunction = modifyStyleFunction(pointSizeStyleFunction);
  788. }
  789. var usesColorSemantic = (styleableProperties.indexOf('COLOR') >= 0);
  790. var usesNormalSemantic = (styleableProperties.indexOf('NORMAL') >= 0);
  791. // Split default properties from user properties
  792. var userProperties = styleableProperties.filter(function(property) { return defaultProperties.indexOf(property) === -1; });
  793. if (usesNormalSemantic && !hasNormals) {
  794. throw new RuntimeError('Style references the NORMAL semantic but the point cloud does not have normals');
  795. }
  796. // Disable vertex attributes that aren't used in the style, enable attributes that are
  797. var styleableShaderAttributes = pointCloud._styleableShaderAttributes;
  798. for (name in styleableShaderAttributes) {
  799. if (styleableShaderAttributes.hasOwnProperty(name)) {
  800. attribute = styleableShaderAttributes[name];
  801. var enabled = (userProperties.indexOf(name) >= 0);
  802. var vertexAttribute = getVertexAttribute(vertexArray, attribute.location);
  803. vertexAttribute.enabled = enabled;
  804. }
  805. }
  806. var usesColors = hasColors && (!hasColorStyle || usesColorSemantic);
  807. if (hasColors) {
  808. // Disable the color vertex attribute if the color style does not reference the color semantic
  809. var colorVertexAttribute = getVertexAttribute(vertexArray, colorLocation);
  810. colorVertexAttribute.enabled = usesColors;
  811. }
  812. var usesNormals = hasNormals && (normalShading || backFaceCulling || usesNormalSemantic);
  813. if (hasNormals) {
  814. // Disable the normal vertex attribute if normals are not used
  815. var normalVertexAttribute = getVertexAttribute(vertexArray, normalLocation);
  816. normalVertexAttribute.enabled = usesNormals;
  817. }
  818. var attributeLocations = {
  819. a_position : positionLocation
  820. };
  821. if (usesColors) {
  822. attributeLocations.a_color = colorLocation;
  823. }
  824. if (usesNormals) {
  825. attributeLocations.a_normal = normalLocation;
  826. }
  827. if (hasBatchIds) {
  828. attributeLocations.a_batchId = batchIdLocation;
  829. }
  830. var attributeDeclarations = '';
  831. var length = userProperties.length;
  832. for (i = 0; i < length; ++i) {
  833. name = userProperties[i];
  834. attribute = styleableShaderAttributes[name];
  835. if (!defined(attribute)) {
  836. throw new RuntimeError('Style references a property "' + name + '" that does not exist or is not styleable.');
  837. }
  838. var componentCount = attribute.componentCount;
  839. var attributeName = 'czm_tiles3d_style_' + name;
  840. var attributeType;
  841. if (componentCount === 1) {
  842. attributeType = 'float';
  843. } else {
  844. attributeType = 'vec' + componentCount;
  845. }
  846. attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n';
  847. attributeLocations[attributeName] = attribute.location;
  848. }
  849. createUniformMap(pointCloud, frameState);
  850. var vs = 'attribute vec3 a_position; \n' +
  851. 'varying vec4 v_color; \n' +
  852. 'uniform vec4 u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier; \n' +
  853. 'uniform vec4 u_constantColor; \n' +
  854. 'uniform vec4 u_highlightColor; \n';
  855. vs += 'float u_pointSize; \n' +
  856. 'float u_time; \n';
  857. if (attenuation) {
  858. vs += 'float u_geometricError; \n' +
  859. 'float u_depthMultiplier; \n';
  860. }
  861. vs += attributeDeclarations;
  862. if (usesColors) {
  863. if (isTranslucent) {
  864. vs += 'attribute vec4 a_color; \n';
  865. } else if (isRGB565) {
  866. vs += 'attribute float a_color; \n' +
  867. 'const float SHIFT_RIGHT_11 = 1.0 / 2048.0; \n' +
  868. 'const float SHIFT_RIGHT_5 = 1.0 / 32.0; \n' +
  869. 'const float SHIFT_LEFT_11 = 2048.0; \n' +
  870. 'const float SHIFT_LEFT_5 = 32.0; \n' +
  871. 'const float NORMALIZE_6 = 1.0 / 64.0; \n' +
  872. 'const float NORMALIZE_5 = 1.0 / 32.0; \n';
  873. } else {
  874. vs += 'attribute vec3 a_color; \n';
  875. }
  876. }
  877. if (usesNormals) {
  878. if (isOctEncoded16P || isOctEncodedDraco) {
  879. vs += 'attribute vec2 a_normal; \n';
  880. } else {
  881. vs += 'attribute vec3 a_normal; \n';
  882. }
  883. }
  884. if (hasBatchIds) {
  885. vs += 'attribute float a_batchId; \n';
  886. }
  887. if (isQuantized || isQuantizedDraco || isOctEncodedDraco) {
  888. vs += 'uniform vec4 u_quantizedVolumeScaleAndOctEncodedRange; \n';
  889. }
  890. if (hasColorStyle) {
  891. vs += colorStyleFunction;
  892. }
  893. if (hasShowStyle) {
  894. vs += showStyleFunction;
  895. }
  896. if (hasPointSizeStyle) {
  897. vs += pointSizeStyleFunction;
  898. }
  899. vs += 'void main() \n' +
  900. '{ \n' +
  901. ' u_pointSize = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.x; \n' +
  902. ' u_time = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.y; \n';
  903. if (attenuation) {
  904. vs += ' u_geometricError = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.z; \n' +
  905. ' u_depthMultiplier = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.w; \n';
  906. }
  907. if (usesColors) {
  908. if (isTranslucent) {
  909. vs += ' vec4 color = a_color; \n';
  910. } else if (isRGB565) {
  911. vs += ' float compressed = a_color; \n' +
  912. ' float r = floor(compressed * SHIFT_RIGHT_11); \n' +
  913. ' compressed -= r * SHIFT_LEFT_11; \n' +
  914. ' float g = floor(compressed * SHIFT_RIGHT_5); \n' +
  915. ' compressed -= g * SHIFT_LEFT_5; \n' +
  916. ' float b = compressed; \n' +
  917. ' vec3 rgb = vec3(r * NORMALIZE_5, g * NORMALIZE_6, b * NORMALIZE_5); \n' +
  918. ' vec4 color = vec4(rgb, 1.0); \n';
  919. } else {
  920. vs += ' vec4 color = vec4(a_color, 1.0); \n';
  921. }
  922. } else {
  923. vs += ' vec4 color = u_constantColor; \n';
  924. }
  925. if (isQuantized || isQuantizedDraco) {
  926. vs += ' vec3 position = a_position * u_quantizedVolumeScaleAndOctEncodedRange.xyz; \n';
  927. } else {
  928. vs += ' vec3 position = a_position; \n';
  929. }
  930. vs += ' vec3 position_absolute = vec3(czm_model * vec4(position, 1.0)); \n';
  931. if (usesNormals) {
  932. if (isOctEncoded16P) {
  933. vs += ' vec3 normal = czm_octDecode(a_normal); \n';
  934. } else if (isOctEncodedDraco) {
  935. // Draco oct-encoding decodes to zxy order
  936. vs += ' vec3 normal = czm_octDecode(a_normal, u_quantizedVolumeScaleAndOctEncodedRange.w).zxy; \n';
  937. } else {
  938. vs += ' vec3 normal = a_normal; \n';
  939. }
  940. vs += ' vec3 normalEC = czm_normal * normal; \n';
  941. } else {
  942. vs += ' vec3 normal = vec3(1.0); \n';
  943. }
  944. if (hasColorStyle) {
  945. vs += ' color = getColorFromStyle(position, position_absolute, color, normal); \n';
  946. }
  947. if (hasShowStyle) {
  948. vs += ' float show = float(getShowFromStyle(position, position_absolute, color, normal)); \n';
  949. }
  950. if (hasPointSizeStyle) {
  951. vs += ' gl_PointSize = getPointSizeFromStyle(position, position_absolute, color, normal) * czm_pixelRatio; \n';
  952. } else if (attenuation) {
  953. vs += ' vec4 positionEC = czm_modelView * vec4(position, 1.0); \n' +
  954. ' float depth = -positionEC.z; \n' +
  955. // compute SSE for this point
  956. ' gl_PointSize = min((u_geometricError / depth) * u_depthMultiplier, u_pointSize); \n';
  957. } else {
  958. vs += ' gl_PointSize = u_pointSize; \n';
  959. }
  960. vs += ' color = color * u_highlightColor; \n';
  961. if (usesNormals && normalShading) {
  962. vs += ' float diffuseStrength = czm_getLambertDiffuse(czm_sunDirectionEC, normalEC); \n' +
  963. ' diffuseStrength = max(diffuseStrength, 0.4); \n' + // Apply some ambient lighting
  964. ' color.xyz *= diffuseStrength; \n';
  965. }
  966. vs += ' v_color = color; \n' +
  967. ' gl_Position = czm_modelViewProjection * vec4(position, 1.0); \n';
  968. if (usesNormals && backFaceCulling) {
  969. vs += ' float visible = step(-normalEC.z, 0.0); \n' +
  970. ' gl_Position *= visible; \n' +
  971. ' gl_PointSize *= visible; \n';
  972. }
  973. if (hasShowStyle) {
  974. vs += ' gl_Position *= show; \n' +
  975. ' gl_PointSize *= show; \n';
  976. }
  977. vs += '} \n';
  978. var fs = 'varying vec4 v_color; \n';
  979. if (hasClippedContent) {
  980. fs += 'uniform sampler2D u_clippingPlanes; \n' +
  981. 'uniform mat4 u_clippingPlanesMatrix; \n' +
  982. 'uniform vec4 u_clippingPlanesEdgeStyle; \n';
  983. fs += '\n';
  984. fs += getClippingFunction(clippingPlanes, context);
  985. fs += '\n';
  986. }
  987. fs += 'void main() \n' +
  988. '{ \n' +
  989. ' gl_FragColor = czm_gammaCorrect(v_color); \n';
  990. if (hasClippedContent) {
  991. fs += getClipAndStyleCode('u_clippingPlanes', 'u_clippingPlanesMatrix', 'u_clippingPlanesEdgeStyle');
  992. }
  993. fs += '} \n';
  994. if (defined(pointCloud._vertexShaderLoaded)) {
  995. vs = pointCloud._vertexShaderLoaded(vs);
  996. }
  997. if (defined(pointCloud._fragmentShaderLoaded)) {
  998. fs = pointCloud._fragmentShaderLoaded(fs);
  999. }
  1000. var drawCommand = pointCloud._drawCommand;
  1001. if (defined(drawCommand.shaderProgram)) {
  1002. // Destroy the old shader
  1003. drawCommand.shaderProgram.destroy();
  1004. }
  1005. drawCommand.shaderProgram = ShaderProgram.fromCache({
  1006. context : context,
  1007. vertexShaderSource : vs,
  1008. fragmentShaderSource : fs,
  1009. attributeLocations : attributeLocations
  1010. });
  1011. try {
  1012. // Check if the shader compiles correctly. If not there is likely a syntax error with the style.
  1013. drawCommand.shaderProgram._bind();
  1014. } catch (error) {
  1015. // Rephrase the error.
  1016. throw new RuntimeError('Error generating style shader: this may be caused by a type mismatch, index out-of-bounds, or other syntax error.');
  1017. }
  1018. }
  1019. function decodeDraco(pointCloud, context) {
  1020. if (pointCloud._decodingState === DecodingState.READY) {
  1021. return false;
  1022. }
  1023. if (pointCloud._decodingState === DecodingState.NEEDS_DECODE) {
  1024. var parsedContent = pointCloud._parsedContent;
  1025. var draco = parsedContent.draco;
  1026. var decodePromise = DracoLoader.decodePointCloud(draco, context);
  1027. if (defined(decodePromise)) {
  1028. pointCloud._decodingState = DecodingState.DECODING;
  1029. decodePromise.then(function(result) {
  1030. pointCloud._decodingState = DecodingState.READY;
  1031. var decodedPositions = defined(result.POSITION) ? result.POSITION.array : undefined;
  1032. var decodedRgb = defined(result.RGB) ? result.RGB.array : undefined;
  1033. var decodedRgba = defined(result.RGBA) ? result.RGBA.array : undefined;
  1034. var decodedNormals = defined(result.NORMAL) ? result.NORMAL.array : undefined;
  1035. var decodedBatchIds = defined(result.BATCH_ID) ? result.BATCH_ID.array : undefined;
  1036. var isQuantizedDraco = defined(decodedPositions) && defined(result.POSITION.data.quantization);
  1037. var isOctEncodedDraco = defined(decodedNormals) && defined(result.NORMAL.data.quantization);
  1038. if (isQuantizedDraco) {
  1039. // Draco quantization range == quantized volume scale - size in meters of the quantized volume
  1040. // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc
  1041. var quantization = result.POSITION.data.quantization;
  1042. var range = quantization.range;
  1043. pointCloud._quantizedVolumeScale = Cartesian3.fromElements(range, range, range);
  1044. pointCloud._quantizedVolumeOffset = Cartesian3.unpack(quantization.minValues);
  1045. pointCloud._quantizedRange = (1 << quantization.quantizationBits) - 1.0;
  1046. pointCloud._isQuantizedDraco = true;
  1047. }
  1048. if (isOctEncodedDraco) {
  1049. pointCloud._octEncodedRange = (1 << result.NORMAL.data.quantization.quantizationBits) - 1.0;
  1050. pointCloud._isOctEncodedDraco = true;
  1051. }
  1052. var styleableProperties = parsedContent.styleableProperties;
  1053. var batchTableProperties = draco.batchTableProperties;
  1054. for (var name in batchTableProperties) {
  1055. if (batchTableProperties.hasOwnProperty(name)) {
  1056. var property = result[name];
  1057. if (!defined(styleableProperties)) {
  1058. styleableProperties = {};
  1059. }
  1060. styleableProperties[name] = {
  1061. typedArray : property.array,
  1062. componentCount : property.data.componentsPerAttribute
  1063. };
  1064. }
  1065. }
  1066. parsedContent.positions = defaultValue(decodedPositions, parsedContent.positions);
  1067. parsedContent.colors = defaultValue(defaultValue(decodedRgba, decodedRgb), parsedContent.colors);
  1068. parsedContent.normals = defaultValue(decodedNormals, parsedContent.normals);
  1069. parsedContent.batchIds = defaultValue(decodedBatchIds, parsedContent.batchIds);
  1070. parsedContent.styleableProperties = styleableProperties;
  1071. }).otherwise(function(error) {
  1072. pointCloud._decodingState = DecodingState.FAILED;
  1073. pointCloud._readyPromise.reject(error);
  1074. });
  1075. }
  1076. }
  1077. return true;
  1078. }
  1079. var scratchComputedTranslation = new Cartesian4();
  1080. var scratchScale = new Cartesian3();
  1081. PointCloud.prototype.update = function(frameState) {
  1082. var context = frameState.context;
  1083. var decoding = decodeDraco(this, context);
  1084. if (decoding) {
  1085. return;
  1086. }
  1087. var shadersDirty = false;
  1088. var modelMatrixDirty = !Matrix4.equals(this._modelMatrix, this.modelMatrix);
  1089. if (this._mode !== frameState.mode) {
  1090. this._mode = frameState.mode;
  1091. modelMatrixDirty = true;
  1092. }
  1093. if (!defined(this._drawCommand)) {
  1094. createResources(this, frameState);
  1095. modelMatrixDirty = true;
  1096. shadersDirty = true;
  1097. this._ready = true;
  1098. this._readyPromise.resolve(this);
  1099. this._parsedContent = undefined; // Unload
  1100. }
  1101. if (modelMatrixDirty) {
  1102. Matrix4.clone(this.modelMatrix, this._modelMatrix);
  1103. var modelMatrix = this._drawCommand.modelMatrix;
  1104. Matrix4.clone(this._modelMatrix, modelMatrix);
  1105. if (defined(this._rtcCenter)) {
  1106. Matrix4.multiplyByTranslation(modelMatrix, this._rtcCenter, modelMatrix);
  1107. }
  1108. if (defined(this._quantizedVolumeOffset)) {
  1109. Matrix4.multiplyByTranslation(modelMatrix, this._quantizedVolumeOffset, modelMatrix);
  1110. }
  1111. if (frameState.mode !== SceneMode.SCENE3D) {
  1112. var projection = frameState.mapProjection;
  1113. var translation = Matrix4.getColumn(modelMatrix, 3, scratchComputedTranslation);
  1114. if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {
  1115. Transforms.basisTo2D(projection, modelMatrix, modelMatrix);
  1116. }
  1117. }
  1118. var boundingSphere = this._drawCommand.boundingVolume;
  1119. BoundingSphere.clone(this._boundingSphere, boundingSphere);
  1120. if (this._cull) {
  1121. var center = boundingSphere.center;
  1122. Matrix4.multiplyByPoint(modelMatrix, center, center);
  1123. var scale = Matrix4.getScale(modelMatrix, scratchScale);
  1124. boundingSphere.radius *= Cartesian3.maximumComponent(scale);
  1125. }
  1126. }
  1127. if (this.clippingPlanesDirty) {
  1128. this.clippingPlanesDirty = false;
  1129. shadersDirty = true;
  1130. }
  1131. if (this._attenuation !== this.attenuation) {
  1132. this._attenuation = this.attenuation;
  1133. shadersDirty = true;
  1134. }
  1135. if (this.backFaceCulling !== this._backFaceCulling) {
  1136. this._backFaceCulling = this.backFaceCulling;
  1137. shadersDirty = true;
  1138. }
  1139. if (this.normalShading !== this._normalShading) {
  1140. this._normalShading = this.normalShading;
  1141. shadersDirty = true;
  1142. }
  1143. if (this._style !== this.style || this.styleDirty) {
  1144. this._style = this.style;
  1145. this.styleDirty = false;
  1146. shadersDirty = true;
  1147. }
  1148. if (shadersDirty) {
  1149. createShaders(this, frameState, this._style);
  1150. }
  1151. this._drawCommand.castShadows = ShadowMode.castShadows(this.shadows);
  1152. this._drawCommand.receiveShadows = ShadowMode.receiveShadows(this.shadows);
  1153. // Update the render state
  1154. var isTranslucent = (this._highlightColor.alpha < 1.0) || (this._constantColor.alpha < 1.0) || this._styleTranslucent;
  1155. this._drawCommand.renderState = isTranslucent ? this._translucentRenderState : this._opaqueRenderState;
  1156. this._drawCommand.pass = isTranslucent ? Pass.TRANSLUCENT : this._opaquePass;
  1157. var commandList = frameState.commandList;
  1158. var passes = frameState.passes;
  1159. if (passes.render || passes.pick) {
  1160. commandList.push(this._drawCommand);
  1161. }
  1162. };
  1163. PointCloud.prototype.isDestroyed = function() {
  1164. return false;
  1165. };
  1166. PointCloud.prototype.destroy = function() {
  1167. var command = this._drawCommand;
  1168. if (defined(command)) {
  1169. command.vertexArray = command.vertexArray && command.vertexArray.destroy();
  1170. command.shaderProgram = command.shaderProgram && command.shaderProgram.destroy();
  1171. }
  1172. return destroyObject(this);
  1173. };
  1174. export default PointCloud;