PointCloud.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637
  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 clone from "../Core/clone.js";
  7. import Color from "../Core/Color.js";
  8. import combine from "../Core/combine.js";
  9. import ComponentDatatype from "../Core/ComponentDatatype.js";
  10. import defaultValue from "../Core/defaultValue.js";
  11. import defined from "../Core/defined.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/CesiumGS/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. Object.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. return undefined;
  166. },
  167. set: function (value) {
  168. this._boundingSphere = BoundingSphere.clone(value, this._boundingSphere);
  169. },
  170. },
  171. });
  172. var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  173. function initialize(pointCloud, options) {
  174. var arrayBuffer = options.arrayBuffer;
  175. var byteOffset = defaultValue(options.byteOffset, 0);
  176. var uint8Array = new Uint8Array(arrayBuffer);
  177. var view = new DataView(arrayBuffer);
  178. byteOffset += sizeOfUint32; // Skip magic
  179. var version = view.getUint32(byteOffset, true);
  180. if (version !== 1) {
  181. throw new RuntimeError(
  182. "Only Point Cloud tile version 1 is supported. Version " +
  183. version +
  184. " is not."
  185. );
  186. }
  187. byteOffset += sizeOfUint32;
  188. // Skip byteLength
  189. byteOffset += sizeOfUint32;
  190. var featureTableJsonByteLength = view.getUint32(byteOffset, true);
  191. if (featureTableJsonByteLength === 0) {
  192. throw new RuntimeError(
  193. "Feature table must have a byte length greater than zero"
  194. );
  195. }
  196. byteOffset += sizeOfUint32;
  197. var featureTableBinaryByteLength = view.getUint32(byteOffset, true);
  198. byteOffset += sizeOfUint32;
  199. var batchTableJsonByteLength = view.getUint32(byteOffset, true);
  200. byteOffset += sizeOfUint32;
  201. var batchTableBinaryByteLength = view.getUint32(byteOffset, true);
  202. byteOffset += sizeOfUint32;
  203. var featureTableString = getStringFromTypedArray(
  204. uint8Array,
  205. byteOffset,
  206. featureTableJsonByteLength
  207. );
  208. var featureTableJson = JSON.parse(featureTableString);
  209. byteOffset += featureTableJsonByteLength;
  210. var featureTableBinary = new Uint8Array(
  211. arrayBuffer,
  212. byteOffset,
  213. featureTableBinaryByteLength
  214. );
  215. byteOffset += featureTableBinaryByteLength;
  216. // Get the batch table JSON and binary
  217. var batchTableJson;
  218. var batchTableBinary;
  219. if (batchTableJsonByteLength > 0) {
  220. // Has a batch table JSON
  221. var batchTableString = getStringFromTypedArray(
  222. uint8Array,
  223. byteOffset,
  224. batchTableJsonByteLength
  225. );
  226. batchTableJson = JSON.parse(batchTableString);
  227. byteOffset += batchTableJsonByteLength;
  228. if (batchTableBinaryByteLength > 0) {
  229. // Has a batch table binary
  230. batchTableBinary = new Uint8Array(
  231. arrayBuffer,
  232. byteOffset,
  233. batchTableBinaryByteLength
  234. );
  235. byteOffset += batchTableBinaryByteLength;
  236. }
  237. }
  238. var featureTable = new Cesium3DTileFeatureTable(
  239. featureTableJson,
  240. featureTableBinary
  241. );
  242. var pointsLength = featureTable.getGlobalProperty("POINTS_LENGTH");
  243. featureTable.featuresLength = pointsLength;
  244. if (!defined(pointsLength)) {
  245. throw new RuntimeError(
  246. "Feature table global property: POINTS_LENGTH must be defined"
  247. );
  248. }
  249. var rtcCenter = featureTable.getGlobalProperty(
  250. "RTC_CENTER",
  251. ComponentDatatype.FLOAT,
  252. 3
  253. );
  254. if (defined(rtcCenter)) {
  255. pointCloud._rtcCenter = Cartesian3.unpack(rtcCenter);
  256. }
  257. var positions;
  258. var colors;
  259. var normals;
  260. var batchIds;
  261. var hasPositions = false;
  262. var hasColors = false;
  263. var hasNormals = false;
  264. var hasBatchIds = false;
  265. var isQuantized = false;
  266. var isTranslucent = false;
  267. var isRGB565 = false;
  268. var isOctEncoded16P = false;
  269. var dracoBuffer;
  270. var dracoFeatureTableProperties;
  271. var dracoBatchTableProperties;
  272. var featureTableDraco = defined(featureTableJson.extensions)
  273. ? featureTableJson.extensions["3DTILES_draco_point_compression"]
  274. : undefined;
  275. var batchTableDraco =
  276. defined(batchTableJson) && defined(batchTableJson.extensions)
  277. ? batchTableJson.extensions["3DTILES_draco_point_compression"]
  278. : undefined;
  279. if (defined(batchTableDraco)) {
  280. dracoBatchTableProperties = batchTableDraco.properties;
  281. }
  282. if (defined(featureTableDraco)) {
  283. dracoFeatureTableProperties = featureTableDraco.properties;
  284. var dracoByteOffset = featureTableDraco.byteOffset;
  285. var dracoByteLength = featureTableDraco.byteLength;
  286. if (
  287. !defined(dracoFeatureTableProperties) ||
  288. !defined(dracoByteOffset) ||
  289. !defined(dracoByteLength)
  290. ) {
  291. throw new RuntimeError(
  292. "Draco properties, byteOffset, and byteLength must be defined"
  293. );
  294. }
  295. dracoBuffer = arraySlice(
  296. featureTableBinary,
  297. dracoByteOffset,
  298. dracoByteOffset + dracoByteLength
  299. );
  300. hasPositions = defined(dracoFeatureTableProperties.POSITION);
  301. hasColors =
  302. defined(dracoFeatureTableProperties.RGB) ||
  303. defined(dracoFeatureTableProperties.RGBA);
  304. hasNormals = defined(dracoFeatureTableProperties.NORMAL);
  305. hasBatchIds = defined(dracoFeatureTableProperties.BATCH_ID);
  306. isTranslucent = defined(dracoFeatureTableProperties.RGBA);
  307. pointCloud._decodingState = DecodingState.NEEDS_DECODE;
  308. }
  309. var draco;
  310. if (defined(dracoBuffer)) {
  311. draco = {
  312. buffer: dracoBuffer,
  313. featureTableProperties: dracoFeatureTableProperties,
  314. batchTableProperties: dracoBatchTableProperties,
  315. properties: combine(
  316. dracoFeatureTableProperties,
  317. dracoBatchTableProperties
  318. ),
  319. dequantizeInShader: pointCloud._dequantizeInShader,
  320. };
  321. }
  322. if (!hasPositions) {
  323. if (defined(featureTableJson.POSITION)) {
  324. positions = featureTable.getPropertyArray(
  325. "POSITION",
  326. ComponentDatatype.FLOAT,
  327. 3
  328. );
  329. hasPositions = true;
  330. } else if (defined(featureTableJson.POSITION_QUANTIZED)) {
  331. positions = featureTable.getPropertyArray(
  332. "POSITION_QUANTIZED",
  333. ComponentDatatype.UNSIGNED_SHORT,
  334. 3
  335. );
  336. isQuantized = true;
  337. hasPositions = true;
  338. var quantizedVolumeScale = featureTable.getGlobalProperty(
  339. "QUANTIZED_VOLUME_SCALE",
  340. ComponentDatatype.FLOAT,
  341. 3
  342. );
  343. if (!defined(quantizedVolumeScale)) {
  344. throw new RuntimeError(
  345. "Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions."
  346. );
  347. }
  348. pointCloud._quantizedVolumeScale = Cartesian3.unpack(
  349. quantizedVolumeScale
  350. );
  351. pointCloud._quantizedRange = (1 << 16) - 1;
  352. var quantizedVolumeOffset = featureTable.getGlobalProperty(
  353. "QUANTIZED_VOLUME_OFFSET",
  354. ComponentDatatype.FLOAT,
  355. 3
  356. );
  357. if (!defined(quantizedVolumeOffset)) {
  358. throw new RuntimeError(
  359. "Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions."
  360. );
  361. }
  362. pointCloud._quantizedVolumeOffset = Cartesian3.unpack(
  363. quantizedVolumeOffset
  364. );
  365. }
  366. }
  367. if (!hasColors) {
  368. if (defined(featureTableJson.RGBA)) {
  369. colors = featureTable.getPropertyArray(
  370. "RGBA",
  371. ComponentDatatype.UNSIGNED_BYTE,
  372. 4
  373. );
  374. isTranslucent = true;
  375. hasColors = true;
  376. } else if (defined(featureTableJson.RGB)) {
  377. colors = featureTable.getPropertyArray(
  378. "RGB",
  379. ComponentDatatype.UNSIGNED_BYTE,
  380. 3
  381. );
  382. hasColors = true;
  383. } else if (defined(featureTableJson.RGB565)) {
  384. colors = featureTable.getPropertyArray(
  385. "RGB565",
  386. ComponentDatatype.UNSIGNED_SHORT,
  387. 1
  388. );
  389. isRGB565 = true;
  390. hasColors = true;
  391. }
  392. }
  393. if (!hasNormals) {
  394. if (defined(featureTableJson.NORMAL)) {
  395. normals = featureTable.getPropertyArray(
  396. "NORMAL",
  397. ComponentDatatype.FLOAT,
  398. 3
  399. );
  400. hasNormals = true;
  401. } else if (defined(featureTableJson.NORMAL_OCT16P)) {
  402. normals = featureTable.getPropertyArray(
  403. "NORMAL_OCT16P",
  404. ComponentDatatype.UNSIGNED_BYTE,
  405. 2
  406. );
  407. isOctEncoded16P = true;
  408. hasNormals = true;
  409. }
  410. }
  411. if (!hasBatchIds) {
  412. if (defined(featureTableJson.BATCH_ID)) {
  413. batchIds = featureTable.getPropertyArray(
  414. "BATCH_ID",
  415. ComponentDatatype.UNSIGNED_SHORT,
  416. 1
  417. );
  418. hasBatchIds = true;
  419. }
  420. }
  421. if (!hasPositions) {
  422. throw new RuntimeError(
  423. "Either POSITION or POSITION_QUANTIZED must be defined."
  424. );
  425. }
  426. if (defined(featureTableJson.CONSTANT_RGBA)) {
  427. var constantRGBA = featureTable.getGlobalProperty(
  428. "CONSTANT_RGBA",
  429. ComponentDatatype.UNSIGNED_BYTE,
  430. 4
  431. );
  432. pointCloud._constantColor = Color.fromBytes(
  433. constantRGBA[0],
  434. constantRGBA[1],
  435. constantRGBA[2],
  436. constantRGBA[3],
  437. pointCloud._constantColor
  438. );
  439. }
  440. if (hasBatchIds) {
  441. var batchLength = featureTable.getGlobalProperty("BATCH_LENGTH");
  442. if (!defined(batchLength)) {
  443. throw new RuntimeError(
  444. "Global property: BATCH_LENGTH must be defined when BATCH_ID is defined."
  445. );
  446. }
  447. if (defined(batchTableBinary)) {
  448. // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
  449. batchTableBinary = new Uint8Array(batchTableBinary);
  450. }
  451. if (defined(pointCloud._batchTableLoaded)) {
  452. pointCloud._batchTableLoaded(
  453. batchLength,
  454. batchTableJson,
  455. batchTableBinary
  456. );
  457. }
  458. }
  459. // If points are not batched and there are per-point properties, use these properties for styling purposes
  460. var styleableProperties;
  461. if (!hasBatchIds && defined(batchTableBinary)) {
  462. styleableProperties = Cesium3DTileBatchTable.getBinaryProperties(
  463. pointsLength,
  464. batchTableJson,
  465. batchTableBinary
  466. );
  467. }
  468. pointCloud._parsedContent = {
  469. positions: positions,
  470. colors: colors,
  471. normals: normals,
  472. batchIds: batchIds,
  473. styleableProperties: styleableProperties,
  474. draco: draco,
  475. };
  476. pointCloud._pointsLength = pointsLength;
  477. pointCloud._isQuantized = isQuantized;
  478. pointCloud._isOctEncoded16P = isOctEncoded16P;
  479. pointCloud._isRGB565 = isRGB565;
  480. pointCloud._isTranslucent = isTranslucent;
  481. pointCloud._hasColors = hasColors;
  482. pointCloud._hasNormals = hasNormals;
  483. pointCloud._hasBatchIds = hasBatchIds;
  484. }
  485. var scratchMin = new Cartesian3();
  486. var scratchMax = new Cartesian3();
  487. var scratchPosition = new Cartesian3();
  488. var randomValues;
  489. function getRandomValues(samplesLength) {
  490. // Use same random values across all runs
  491. if (!defined(randomValues)) {
  492. CesiumMath.setRandomNumberSeed(0);
  493. randomValues = new Array(samplesLength);
  494. for (var i = 0; i < samplesLength; ++i) {
  495. randomValues[i] = CesiumMath.nextRandomNumber();
  496. }
  497. }
  498. return randomValues;
  499. }
  500. function computeApproximateBoundingSphereFromPositions(positions) {
  501. var maximumSamplesLength = 20;
  502. var pointsLength = positions.length / 3;
  503. var samplesLength = Math.min(pointsLength, maximumSamplesLength);
  504. var randomValues = getRandomValues(maximumSamplesLength);
  505. var maxValue = Number.MAX_VALUE;
  506. var minValue = -Number.MAX_VALUE;
  507. var min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin);
  508. var max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax);
  509. for (var i = 0; i < samplesLength; ++i) {
  510. var index = Math.floor(randomValues[i] * pointsLength);
  511. var position = Cartesian3.unpack(positions, index * 3, scratchPosition);
  512. Cartesian3.minimumByComponent(min, position, min);
  513. Cartesian3.maximumByComponent(max, position, max);
  514. }
  515. var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
  516. boundingSphere.radius += CesiumMath.EPSILON2; // To avoid radius of zero
  517. return boundingSphere;
  518. }
  519. function prepareVertexAttribute(typedArray, name) {
  520. // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT.
  521. var componentDatatype = ComponentDatatype.fromTypedArray(typedArray);
  522. if (
  523. componentDatatype === ComponentDatatype.INT ||
  524. componentDatatype === ComponentDatatype.UNSIGNED_INT ||
  525. componentDatatype === ComponentDatatype.DOUBLE
  526. ) {
  527. oneTimeWarning(
  528. "Cast pnts property to floats",
  529. 'Point cloud property "' +
  530. name +
  531. '" 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.'
  532. );
  533. return new Float32Array(typedArray);
  534. }
  535. return typedArray;
  536. }
  537. var scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier = new Cartesian4();
  538. var scratchQuantizedVolumeScaleAndOctEncodedRange = new Cartesian4();
  539. var scratchColor = new Color();
  540. var positionLocation = 0;
  541. var colorLocation = 1;
  542. var normalLocation = 2;
  543. var batchIdLocation = 3;
  544. var numberOfAttributes = 4;
  545. var scratchClippingPlaneMatrix = new Matrix4();
  546. function createResources(pointCloud, frameState) {
  547. var context = frameState.context;
  548. var parsedContent = pointCloud._parsedContent;
  549. var pointsLength = pointCloud._pointsLength;
  550. var positions = parsedContent.positions;
  551. var colors = parsedContent.colors;
  552. var normals = parsedContent.normals;
  553. var batchIds = parsedContent.batchIds;
  554. var styleableProperties = parsedContent.styleableProperties;
  555. var hasStyleableProperties = defined(styleableProperties);
  556. var isQuantized = pointCloud._isQuantized;
  557. var isQuantizedDraco = pointCloud._isQuantizedDraco;
  558. var isOctEncoded16P = pointCloud._isOctEncoded16P;
  559. var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  560. var quantizedRange = pointCloud._quantizedRange;
  561. var octEncodedRange = pointCloud._octEncodedRange;
  562. var isRGB565 = pointCloud._isRGB565;
  563. var isTranslucent = pointCloud._isTranslucent;
  564. var hasColors = pointCloud._hasColors;
  565. var hasNormals = pointCloud._hasNormals;
  566. var hasBatchIds = pointCloud._hasBatchIds;
  567. var componentsPerAttribute;
  568. var componentDatatype;
  569. var styleableVertexAttributes = [];
  570. var styleableShaderAttributes = {};
  571. pointCloud._styleableShaderAttributes = styleableShaderAttributes;
  572. if (hasStyleableProperties) {
  573. var attributeLocation = numberOfAttributes;
  574. for (var name in styleableProperties) {
  575. if (styleableProperties.hasOwnProperty(name)) {
  576. var property = styleableProperties[name];
  577. var typedArray = prepareVertexAttribute(property.typedArray, name);
  578. componentsPerAttribute = property.componentCount;
  579. componentDatatype = ComponentDatatype.fromTypedArray(typedArray);
  580. var vertexBuffer = Buffer.createVertexBuffer({
  581. context: context,
  582. typedArray: typedArray,
  583. usage: BufferUsage.STATIC_DRAW,
  584. });
  585. pointCloud._geometryByteLength += vertexBuffer.sizeInBytes;
  586. var vertexAttribute = {
  587. index: attributeLocation,
  588. vertexBuffer: vertexBuffer,
  589. componentsPerAttribute: componentsPerAttribute,
  590. componentDatatype: componentDatatype,
  591. normalize: false,
  592. offsetInBytes: 0,
  593. strideInBytes: 0,
  594. };
  595. styleableVertexAttributes.push(vertexAttribute);
  596. styleableShaderAttributes[name] = {
  597. location: attributeLocation,
  598. componentCount: componentsPerAttribute,
  599. };
  600. ++attributeLocation;
  601. }
  602. }
  603. }
  604. var positionsVertexBuffer = Buffer.createVertexBuffer({
  605. context: context,
  606. typedArray: positions,
  607. usage: BufferUsage.STATIC_DRAW,
  608. });
  609. pointCloud._geometryByteLength += positionsVertexBuffer.sizeInBytes;
  610. var colorsVertexBuffer;
  611. if (hasColors) {
  612. colorsVertexBuffer = Buffer.createVertexBuffer({
  613. context: context,
  614. typedArray: colors,
  615. usage: BufferUsage.STATIC_DRAW,
  616. });
  617. pointCloud._geometryByteLength += colorsVertexBuffer.sizeInBytes;
  618. }
  619. var normalsVertexBuffer;
  620. if (hasNormals) {
  621. normalsVertexBuffer = Buffer.createVertexBuffer({
  622. context: context,
  623. typedArray: normals,
  624. usage: BufferUsage.STATIC_DRAW,
  625. });
  626. pointCloud._geometryByteLength += normalsVertexBuffer.sizeInBytes;
  627. }
  628. var batchIdsVertexBuffer;
  629. if (hasBatchIds) {
  630. batchIds = prepareVertexAttribute(batchIds, "batchIds");
  631. batchIdsVertexBuffer = Buffer.createVertexBuffer({
  632. context: context,
  633. typedArray: batchIds,
  634. usage: BufferUsage.STATIC_DRAW,
  635. });
  636. pointCloud._geometryByteLength += batchIdsVertexBuffer.sizeInBytes;
  637. }
  638. var attributes = [];
  639. if (isQuantized) {
  640. componentDatatype = ComponentDatatype.UNSIGNED_SHORT;
  641. } else if (isQuantizedDraco) {
  642. componentDatatype =
  643. quantizedRange <= 255
  644. ? ComponentDatatype.UNSIGNED_BYTE
  645. : ComponentDatatype.UNSIGNED_SHORT;
  646. } else {
  647. componentDatatype = ComponentDatatype.FLOAT;
  648. }
  649. attributes.push({
  650. index: positionLocation,
  651. vertexBuffer: positionsVertexBuffer,
  652. componentsPerAttribute: 3,
  653. componentDatatype: componentDatatype,
  654. normalize: false,
  655. offsetInBytes: 0,
  656. strideInBytes: 0,
  657. });
  658. if (pointCloud._cull) {
  659. if (isQuantized || isQuantizedDraco) {
  660. pointCloud._boundingSphere = BoundingSphere.fromCornerPoints(
  661. Cartesian3.ZERO,
  662. pointCloud._quantizedVolumeScale
  663. );
  664. } else {
  665. pointCloud._boundingSphere = computeApproximateBoundingSphereFromPositions(
  666. positions
  667. );
  668. }
  669. }
  670. if (hasColors) {
  671. if (isRGB565) {
  672. attributes.push({
  673. index: colorLocation,
  674. vertexBuffer: colorsVertexBuffer,
  675. componentsPerAttribute: 1,
  676. componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
  677. normalize: false,
  678. offsetInBytes: 0,
  679. strideInBytes: 0,
  680. });
  681. } else {
  682. var colorComponentsPerAttribute = isTranslucent ? 4 : 3;
  683. attributes.push({
  684. index: colorLocation,
  685. vertexBuffer: colorsVertexBuffer,
  686. componentsPerAttribute: colorComponentsPerAttribute,
  687. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  688. normalize: true,
  689. offsetInBytes: 0,
  690. strideInBytes: 0,
  691. });
  692. }
  693. }
  694. if (hasNormals) {
  695. if (isOctEncoded16P) {
  696. componentsPerAttribute = 2;
  697. componentDatatype = ComponentDatatype.UNSIGNED_BYTE;
  698. } else if (isOctEncodedDraco) {
  699. componentsPerAttribute = 2;
  700. componentDatatype =
  701. octEncodedRange <= 255
  702. ? ComponentDatatype.UNSIGNED_BYTE
  703. : ComponentDatatype.UNSIGNED_SHORT;
  704. } else {
  705. componentsPerAttribute = 3;
  706. componentDatatype = ComponentDatatype.FLOAT;
  707. }
  708. attributes.push({
  709. index: normalLocation,
  710. vertexBuffer: normalsVertexBuffer,
  711. componentsPerAttribute: componentsPerAttribute,
  712. componentDatatype: componentDatatype,
  713. normalize: false,
  714. offsetInBytes: 0,
  715. strideInBytes: 0,
  716. });
  717. }
  718. if (hasBatchIds) {
  719. attributes.push({
  720. index: batchIdLocation,
  721. vertexBuffer: batchIdsVertexBuffer,
  722. componentsPerAttribute: 1,
  723. componentDatatype: ComponentDatatype.fromTypedArray(batchIds),
  724. normalize: false,
  725. offsetInBytes: 0,
  726. strideInBytes: 0,
  727. });
  728. }
  729. if (hasStyleableProperties) {
  730. attributes = attributes.concat(styleableVertexAttributes);
  731. }
  732. var vertexArray = new VertexArray({
  733. context: context,
  734. attributes: attributes,
  735. });
  736. var opaqueRenderState = {
  737. depthTest: {
  738. enabled: true,
  739. },
  740. };
  741. if (pointCloud._opaquePass === Pass.CESIUM_3D_TILE) {
  742. opaqueRenderState.stencilTest = StencilConstants.setCesium3DTileBit();
  743. opaqueRenderState.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
  744. }
  745. pointCloud._opaqueRenderState = RenderState.fromCache(opaqueRenderState);
  746. pointCloud._translucentRenderState = RenderState.fromCache({
  747. depthTest: {
  748. enabled: true,
  749. },
  750. depthMask: false,
  751. blending: BlendingState.ALPHA_BLEND,
  752. });
  753. pointCloud._drawCommand = new DrawCommand({
  754. boundingVolume: new BoundingSphere(),
  755. cull: pointCloud._cull,
  756. modelMatrix: new Matrix4(),
  757. primitiveType: PrimitiveType.POINTS,
  758. vertexArray: vertexArray,
  759. count: pointsLength,
  760. shaderProgram: undefined, // Updated in createShaders
  761. uniformMap: undefined, // Updated in createShaders
  762. renderState: isTranslucent
  763. ? pointCloud._translucentRenderState
  764. : pointCloud._opaqueRenderState,
  765. pass: isTranslucent ? Pass.TRANSLUCENT : pointCloud._opaquePass,
  766. owner: pointCloud,
  767. castShadows: false,
  768. receiveShadows: false,
  769. pickId: pointCloud._pickIdLoaded(),
  770. });
  771. }
  772. function createUniformMap(pointCloud, frameState) {
  773. var context = frameState.context;
  774. var isQuantized = pointCloud._isQuantized;
  775. var isQuantizedDraco = pointCloud._isQuantizedDraco;
  776. var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  777. var uniformMap = {
  778. u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier: function () {
  779. var scratch = scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier;
  780. scratch.x = pointCloud._attenuation
  781. ? pointCloud.maximumAttenuation
  782. : pointCloud._pointSize;
  783. scratch.x *= frameState.pixelRatio;
  784. scratch.y = pointCloud.time;
  785. if (pointCloud._attenuation) {
  786. var frustum = frameState.camera.frustum;
  787. var depthMultiplier;
  788. // Attenuation is maximumAttenuation in 2D/ortho
  789. if (
  790. frameState.mode === SceneMode.SCENE2D ||
  791. frustum instanceof OrthographicFrustum
  792. ) {
  793. depthMultiplier = Number.POSITIVE_INFINITY;
  794. } else {
  795. depthMultiplier =
  796. context.drawingBufferHeight /
  797. frameState.camera.frustum.sseDenominator;
  798. }
  799. scratch.z = pointCloud.geometricError * pointCloud.geometricErrorScale;
  800. scratch.w = depthMultiplier;
  801. }
  802. return scratch;
  803. },
  804. u_highlightColor: function () {
  805. return pointCloud._highlightColor;
  806. },
  807. u_constantColor: function () {
  808. return pointCloud._constantColor;
  809. },
  810. u_clippingPlanes: function () {
  811. var clippingPlanes = pointCloud.clippingPlanes;
  812. var isClipped = pointCloud.isClipped;
  813. return isClipped ? clippingPlanes.texture : context.defaultTexture;
  814. },
  815. u_clippingPlanesEdgeStyle: function () {
  816. var clippingPlanes = pointCloud.clippingPlanes;
  817. if (!defined(clippingPlanes)) {
  818. return Color.TRANSPARENT;
  819. }
  820. var style = Color.clone(clippingPlanes.edgeColor, scratchColor);
  821. style.alpha = clippingPlanes.edgeWidth;
  822. return style;
  823. },
  824. u_clippingPlanesMatrix: function () {
  825. var clippingPlanes = pointCloud.clippingPlanes;
  826. if (!defined(clippingPlanes)) {
  827. return Matrix4.IDENTITY;
  828. }
  829. var clippingPlanesOriginMatrix = defaultValue(
  830. pointCloud.clippingPlanesOriginMatrix,
  831. pointCloud._modelMatrix
  832. );
  833. Matrix4.multiply(
  834. context.uniformState.view3D,
  835. clippingPlanesOriginMatrix,
  836. scratchClippingPlaneMatrix
  837. );
  838. return Matrix4.multiply(
  839. scratchClippingPlaneMatrix,
  840. clippingPlanes.modelMatrix,
  841. scratchClippingPlaneMatrix
  842. );
  843. },
  844. };
  845. if (isQuantized || isQuantizedDraco || isOctEncodedDraco) {
  846. uniformMap = combine(uniformMap, {
  847. u_quantizedVolumeScaleAndOctEncodedRange: function () {
  848. var scratch = scratchQuantizedVolumeScaleAndOctEncodedRange;
  849. if (defined(pointCloud._quantizedVolumeScale)) {
  850. var scale = Cartesian3.clone(
  851. pointCloud._quantizedVolumeScale,
  852. scratch
  853. );
  854. Cartesian3.divideByScalar(scale, pointCloud._quantizedRange, scratch);
  855. }
  856. scratch.w = pointCloud._octEncodedRange;
  857. return scratch;
  858. },
  859. });
  860. }
  861. if (defined(pointCloud._uniformMapLoaded)) {
  862. uniformMap = pointCloud._uniformMapLoaded(uniformMap);
  863. }
  864. pointCloud._drawCommand.uniformMap = uniformMap;
  865. }
  866. function getStyleablePropertyIds(source, propertyIds) {
  867. // Get all the property IDs used by this style
  868. var regex = /czm_3dtiles_property_(\d+)/g;
  869. var matches = regex.exec(source);
  870. while (matches !== null) {
  871. var id = parseInt(matches[1]);
  872. if (propertyIds.indexOf(id) === -1) {
  873. propertyIds.push(id);
  874. }
  875. matches = regex.exec(source);
  876. }
  877. }
  878. function getBuiltinPropertyNames(source, propertyNames) {
  879. // Get all the builtin property names used by this style
  880. var regex = /czm_3dtiles_builtin_property_(\w+)/g;
  881. var matches = regex.exec(source);
  882. while (matches !== null) {
  883. var name = matches[1];
  884. if (propertyNames.indexOf(name) === -1) {
  885. propertyNames.push(name);
  886. }
  887. matches = regex.exec(source);
  888. }
  889. }
  890. function getVertexAttribute(vertexArray, index) {
  891. var numberOfAttributes = vertexArray.numberOfAttributes;
  892. for (var i = 0; i < numberOfAttributes; ++i) {
  893. var attribute = vertexArray.getAttribute(i);
  894. if (attribute.index === index) {
  895. return attribute;
  896. }
  897. }
  898. }
  899. var builtinPropertyNameMap = {
  900. POSITION: "czm_3dtiles_builtin_property_POSITION",
  901. POSITION_ABSOLUTE: "czm_3dtiles_builtin_property_POSITION_ABSOLUTE",
  902. COLOR: "czm_3dtiles_builtin_property_COLOR",
  903. NORMAL: "czm_3dtiles_builtin_property_NORMAL",
  904. };
  905. function modifyStyleFunction(source) {
  906. // Edit the function header to accept the point position, color, and normal
  907. var functionHeader =
  908. "(" +
  909. "vec3 czm_3dtiles_builtin_property_POSITION, " +
  910. "vec3 czm_3dtiles_builtin_property_POSITION_ABSOLUTE, " +
  911. "vec4 czm_3dtiles_builtin_property_COLOR, " +
  912. "vec3 czm_3dtiles_builtin_property_NORMAL" +
  913. ")";
  914. return source.replace("()", functionHeader);
  915. }
  916. function createShaders(pointCloud, frameState, style) {
  917. var i;
  918. var name;
  919. var attribute;
  920. var context = frameState.context;
  921. var hasStyle = defined(style);
  922. var isQuantized = pointCloud._isQuantized;
  923. var isQuantizedDraco = pointCloud._isQuantizedDraco;
  924. var isOctEncoded16P = pointCloud._isOctEncoded16P;
  925. var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  926. var isRGB565 = pointCloud._isRGB565;
  927. var isTranslucent = pointCloud._isTranslucent;
  928. var hasColors = pointCloud._hasColors;
  929. var hasNormals = pointCloud._hasNormals;
  930. var hasBatchIds = pointCloud._hasBatchIds;
  931. var backFaceCulling = pointCloud._backFaceCulling;
  932. var normalShading = pointCloud._normalShading;
  933. var vertexArray = pointCloud._drawCommand.vertexArray;
  934. var clippingPlanes = pointCloud.clippingPlanes;
  935. var attenuation = pointCloud._attenuation;
  936. var colorStyleFunction;
  937. var showStyleFunction;
  938. var pointSizeStyleFunction;
  939. var styleTranslucent = isTranslucent;
  940. var propertyNameMap = clone(builtinPropertyNameMap);
  941. var propertyIdToAttributeMap = {};
  942. var styleableShaderAttributes = pointCloud._styleableShaderAttributes;
  943. for (name in styleableShaderAttributes) {
  944. if (styleableShaderAttributes.hasOwnProperty(name)) {
  945. attribute = styleableShaderAttributes[name];
  946. propertyNameMap[name] = "czm_3dtiles_property_" + attribute.location;
  947. propertyIdToAttributeMap[attribute.location] = attribute;
  948. }
  949. }
  950. if (hasStyle) {
  951. var shaderState = {
  952. translucent: false,
  953. };
  954. colorStyleFunction = style.getColorShaderFunction(
  955. "getColorFromStyle",
  956. propertyNameMap,
  957. shaderState
  958. );
  959. showStyleFunction = style.getShowShaderFunction(
  960. "getShowFromStyle",
  961. propertyNameMap,
  962. shaderState
  963. );
  964. pointSizeStyleFunction = style.getPointSizeShaderFunction(
  965. "getPointSizeFromStyle",
  966. propertyNameMap,
  967. shaderState
  968. );
  969. if (defined(colorStyleFunction) && shaderState.translucent) {
  970. styleTranslucent = true;
  971. }
  972. }
  973. pointCloud._styleTranslucent = styleTranslucent;
  974. var hasColorStyle = defined(colorStyleFunction);
  975. var hasShowStyle = defined(showStyleFunction);
  976. var hasPointSizeStyle = defined(pointSizeStyleFunction);
  977. var hasClippedContent = pointCloud.isClipped;
  978. // Get the properties in use by the style
  979. var styleablePropertyIds = [];
  980. var builtinPropertyNames = [];
  981. if (hasColorStyle) {
  982. getStyleablePropertyIds(colorStyleFunction, styleablePropertyIds);
  983. getBuiltinPropertyNames(colorStyleFunction, builtinPropertyNames);
  984. colorStyleFunction = modifyStyleFunction(colorStyleFunction);
  985. }
  986. if (hasShowStyle) {
  987. getStyleablePropertyIds(showStyleFunction, styleablePropertyIds);
  988. getBuiltinPropertyNames(showStyleFunction, builtinPropertyNames);
  989. showStyleFunction = modifyStyleFunction(showStyleFunction);
  990. }
  991. if (hasPointSizeStyle) {
  992. getStyleablePropertyIds(pointSizeStyleFunction, styleablePropertyIds);
  993. getBuiltinPropertyNames(pointSizeStyleFunction, builtinPropertyNames);
  994. pointSizeStyleFunction = modifyStyleFunction(pointSizeStyleFunction);
  995. }
  996. var usesColorSemantic = builtinPropertyNames.indexOf("COLOR") >= 0;
  997. var usesNormalSemantic = builtinPropertyNames.indexOf("NORMAL") >= 0;
  998. if (usesNormalSemantic && !hasNormals) {
  999. throw new RuntimeError(
  1000. "Style references the NORMAL semantic but the point cloud does not have normals"
  1001. );
  1002. }
  1003. // Disable vertex attributes that aren't used in the style, enable attributes that are
  1004. for (name in styleableShaderAttributes) {
  1005. if (styleableShaderAttributes.hasOwnProperty(name)) {
  1006. attribute = styleableShaderAttributes[name];
  1007. var enabled = styleablePropertyIds.indexOf(attribute.location) >= 0;
  1008. var vertexAttribute = getVertexAttribute(vertexArray, attribute.location);
  1009. vertexAttribute.enabled = enabled;
  1010. }
  1011. }
  1012. var usesColors = hasColors && (!hasColorStyle || usesColorSemantic);
  1013. if (hasColors) {
  1014. // Disable the color vertex attribute if the color style does not reference the color semantic
  1015. var colorVertexAttribute = getVertexAttribute(vertexArray, colorLocation);
  1016. colorVertexAttribute.enabled = usesColors;
  1017. }
  1018. var usesNormals =
  1019. hasNormals && (normalShading || backFaceCulling || usesNormalSemantic);
  1020. if (hasNormals) {
  1021. // Disable the normal vertex attribute if normals are not used
  1022. var normalVertexAttribute = getVertexAttribute(vertexArray, normalLocation);
  1023. normalVertexAttribute.enabled = usesNormals;
  1024. }
  1025. var attributeLocations = {
  1026. a_position: positionLocation,
  1027. };
  1028. if (usesColors) {
  1029. attributeLocations.a_color = colorLocation;
  1030. }
  1031. if (usesNormals) {
  1032. attributeLocations.a_normal = normalLocation;
  1033. }
  1034. if (hasBatchIds) {
  1035. attributeLocations.a_batchId = batchIdLocation;
  1036. }
  1037. var attributeDeclarations = "";
  1038. var length = styleablePropertyIds.length;
  1039. for (i = 0; i < length; ++i) {
  1040. var propertyId = styleablePropertyIds[i];
  1041. attribute = propertyIdToAttributeMap[propertyId];
  1042. var componentCount = attribute.componentCount;
  1043. var attributeName = "czm_3dtiles_property_" + propertyId;
  1044. var attributeType;
  1045. if (componentCount === 1) {
  1046. attributeType = "float";
  1047. } else {
  1048. attributeType = "vec" + componentCount;
  1049. }
  1050. attributeDeclarations +=
  1051. "attribute " + attributeType + " " + attributeName + "; \n";
  1052. attributeLocations[attributeName] = attribute.location;
  1053. }
  1054. createUniformMap(pointCloud, frameState);
  1055. var vs =
  1056. "attribute vec3 a_position; \n" +
  1057. "varying vec4 v_color; \n" +
  1058. "uniform vec4 u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier; \n" +
  1059. "uniform vec4 u_constantColor; \n" +
  1060. "uniform vec4 u_highlightColor; \n";
  1061. vs += "float u_pointSize; \n" + "float u_time; \n";
  1062. if (attenuation) {
  1063. vs += "float u_geometricError; \n" + "float u_depthMultiplier; \n";
  1064. }
  1065. vs += attributeDeclarations;
  1066. if (usesColors) {
  1067. if (isTranslucent) {
  1068. vs += "attribute vec4 a_color; \n";
  1069. } else if (isRGB565) {
  1070. vs +=
  1071. "attribute float a_color; \n" +
  1072. "const float SHIFT_RIGHT_11 = 1.0 / 2048.0; \n" +
  1073. "const float SHIFT_RIGHT_5 = 1.0 / 32.0; \n" +
  1074. "const float SHIFT_LEFT_11 = 2048.0; \n" +
  1075. "const float SHIFT_LEFT_5 = 32.0; \n" +
  1076. "const float NORMALIZE_6 = 1.0 / 64.0; \n" +
  1077. "const float NORMALIZE_5 = 1.0 / 32.0; \n";
  1078. } else {
  1079. vs += "attribute vec3 a_color; \n";
  1080. }
  1081. }
  1082. if (usesNormals) {
  1083. if (isOctEncoded16P || isOctEncodedDraco) {
  1084. vs += "attribute vec2 a_normal; \n";
  1085. } else {
  1086. vs += "attribute vec3 a_normal; \n";
  1087. }
  1088. }
  1089. if (hasBatchIds) {
  1090. vs += "attribute float a_batchId; \n";
  1091. }
  1092. if (isQuantized || isQuantizedDraco || isOctEncodedDraco) {
  1093. vs += "uniform vec4 u_quantizedVolumeScaleAndOctEncodedRange; \n";
  1094. }
  1095. if (hasColorStyle) {
  1096. vs += colorStyleFunction;
  1097. }
  1098. if (hasShowStyle) {
  1099. vs += showStyleFunction;
  1100. }
  1101. if (hasPointSizeStyle) {
  1102. vs += pointSizeStyleFunction;
  1103. }
  1104. vs +=
  1105. "void main() \n" +
  1106. "{ \n" +
  1107. " u_pointSize = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.x; \n" +
  1108. " u_time = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.y; \n";
  1109. if (attenuation) {
  1110. vs +=
  1111. " u_geometricError = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.z; \n" +
  1112. " u_depthMultiplier = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.w; \n";
  1113. }
  1114. if (usesColors) {
  1115. if (isTranslucent) {
  1116. vs += " vec4 color = a_color; \n";
  1117. } else if (isRGB565) {
  1118. vs +=
  1119. " float compressed = a_color; \n" +
  1120. " float r = floor(compressed * SHIFT_RIGHT_11); \n" +
  1121. " compressed -= r * SHIFT_LEFT_11; \n" +
  1122. " float g = floor(compressed * SHIFT_RIGHT_5); \n" +
  1123. " compressed -= g * SHIFT_LEFT_5; \n" +
  1124. " float b = compressed; \n" +
  1125. " vec3 rgb = vec3(r * NORMALIZE_5, g * NORMALIZE_6, b * NORMALIZE_5); \n" +
  1126. " vec4 color = vec4(rgb, 1.0); \n";
  1127. } else {
  1128. vs += " vec4 color = vec4(a_color, 1.0); \n";
  1129. }
  1130. } else {
  1131. vs += " vec4 color = u_constantColor; \n";
  1132. }
  1133. if (isQuantized || isQuantizedDraco) {
  1134. vs +=
  1135. " vec3 position = a_position * u_quantizedVolumeScaleAndOctEncodedRange.xyz; \n";
  1136. } else {
  1137. vs += " vec3 position = a_position; \n";
  1138. }
  1139. vs +=
  1140. " vec3 position_absolute = vec3(czm_model * vec4(position, 1.0)); \n";
  1141. if (usesNormals) {
  1142. if (isOctEncoded16P) {
  1143. vs += " vec3 normal = czm_octDecode(a_normal); \n";
  1144. } else if (isOctEncodedDraco) {
  1145. // Draco oct-encoding decodes to zxy order
  1146. vs +=
  1147. " vec3 normal = czm_octDecode(a_normal, u_quantizedVolumeScaleAndOctEncodedRange.w).zxy; \n";
  1148. } else {
  1149. vs += " vec3 normal = a_normal; \n";
  1150. }
  1151. vs += " vec3 normalEC = czm_normal * normal; \n";
  1152. } else {
  1153. vs += " vec3 normal = vec3(1.0); \n";
  1154. }
  1155. if (hasColorStyle) {
  1156. vs +=
  1157. " color = getColorFromStyle(position, position_absolute, color, normal); \n";
  1158. }
  1159. if (hasShowStyle) {
  1160. vs +=
  1161. " float show = float(getShowFromStyle(position, position_absolute, color, normal)); \n";
  1162. }
  1163. if (hasPointSizeStyle) {
  1164. vs +=
  1165. " gl_PointSize = getPointSizeFromStyle(position, position_absolute, color, normal) * czm_pixelRatio; \n";
  1166. } else if (attenuation) {
  1167. vs +=
  1168. " vec4 positionEC = czm_modelView * vec4(position, 1.0); \n" +
  1169. " float depth = -positionEC.z; \n" +
  1170. // compute SSE for this point
  1171. " gl_PointSize = min((u_geometricError / depth) * u_depthMultiplier, u_pointSize); \n";
  1172. } else {
  1173. vs += " gl_PointSize = u_pointSize; \n";
  1174. }
  1175. vs += " color = color * u_highlightColor; \n";
  1176. if (usesNormals && normalShading) {
  1177. vs +=
  1178. " float diffuseStrength = czm_getLambertDiffuse(czm_lightDirectionEC, normalEC); \n" +
  1179. " diffuseStrength = max(diffuseStrength, 0.4); \n" + // Apply some ambient lighting
  1180. " color.xyz *= diffuseStrength * czm_lightColor; \n";
  1181. }
  1182. vs +=
  1183. " v_color = color; \n" +
  1184. " gl_Position = czm_modelViewProjection * vec4(position, 1.0); \n";
  1185. if (usesNormals && backFaceCulling) {
  1186. vs +=
  1187. " float visible = step(-normalEC.z, 0.0); \n" +
  1188. " gl_Position *= visible; \n" +
  1189. " gl_PointSize *= visible; \n";
  1190. }
  1191. if (hasShowStyle) {
  1192. vs +=
  1193. " gl_Position.w *= float(show); \n" +
  1194. " gl_PointSize *= float(show); \n";
  1195. }
  1196. vs += "} \n";
  1197. var fs = "varying vec4 v_color; \n";
  1198. if (hasClippedContent) {
  1199. fs +=
  1200. "uniform highp sampler2D u_clippingPlanes; \n" +
  1201. "uniform mat4 u_clippingPlanesMatrix; \n" +
  1202. "uniform vec4 u_clippingPlanesEdgeStyle; \n";
  1203. fs += "\n";
  1204. fs += getClippingFunction(clippingPlanes, context);
  1205. fs += "\n";
  1206. }
  1207. fs +=
  1208. "void main() \n" +
  1209. "{ \n" +
  1210. " gl_FragColor = czm_gammaCorrect(v_color); \n";
  1211. if (hasClippedContent) {
  1212. fs += getClipAndStyleCode(
  1213. "u_clippingPlanes",
  1214. "u_clippingPlanesMatrix",
  1215. "u_clippingPlanesEdgeStyle"
  1216. );
  1217. }
  1218. fs += "} \n";
  1219. if (defined(pointCloud._vertexShaderLoaded)) {
  1220. vs = pointCloud._vertexShaderLoaded(vs);
  1221. }
  1222. if (defined(pointCloud._fragmentShaderLoaded)) {
  1223. fs = pointCloud._fragmentShaderLoaded(fs);
  1224. }
  1225. var drawCommand = pointCloud._drawCommand;
  1226. if (defined(drawCommand.shaderProgram)) {
  1227. // Destroy the old shader
  1228. drawCommand.shaderProgram.destroy();
  1229. }
  1230. drawCommand.shaderProgram = ShaderProgram.fromCache({
  1231. context: context,
  1232. vertexShaderSource: vs,
  1233. fragmentShaderSource: fs,
  1234. attributeLocations: attributeLocations,
  1235. });
  1236. try {
  1237. // Check if the shader compiles correctly. If not there is likely a syntax error with the style.
  1238. drawCommand.shaderProgram._bind();
  1239. } catch (error) {
  1240. // Rephrase the error.
  1241. throw new RuntimeError(
  1242. "Error generating style shader: this may be caused by a type mismatch, index out-of-bounds, or other syntax error."
  1243. );
  1244. }
  1245. }
  1246. function decodeDraco(pointCloud, context) {
  1247. if (pointCloud._decodingState === DecodingState.READY) {
  1248. return false;
  1249. }
  1250. if (pointCloud._decodingState === DecodingState.NEEDS_DECODE) {
  1251. var parsedContent = pointCloud._parsedContent;
  1252. var draco = parsedContent.draco;
  1253. var decodePromise = DracoLoader.decodePointCloud(draco, context);
  1254. if (defined(decodePromise)) {
  1255. pointCloud._decodingState = DecodingState.DECODING;
  1256. decodePromise
  1257. .then(function (result) {
  1258. pointCloud._decodingState = DecodingState.READY;
  1259. var decodedPositions = defined(result.POSITION)
  1260. ? result.POSITION.array
  1261. : undefined;
  1262. var decodedRgb = defined(result.RGB) ? result.RGB.array : undefined;
  1263. var decodedRgba = defined(result.RGBA)
  1264. ? result.RGBA.array
  1265. : undefined;
  1266. var decodedNormals = defined(result.NORMAL)
  1267. ? result.NORMAL.array
  1268. : undefined;
  1269. var decodedBatchIds = defined(result.BATCH_ID)
  1270. ? result.BATCH_ID.array
  1271. : undefined;
  1272. var isQuantizedDraco =
  1273. defined(decodedPositions) &&
  1274. defined(result.POSITION.data.quantization);
  1275. var isOctEncodedDraco =
  1276. defined(decodedNormals) && defined(result.NORMAL.data.quantization);
  1277. if (isQuantizedDraco) {
  1278. // Draco quantization range == quantized volume scale - size in meters of the quantized volume
  1279. // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc
  1280. var quantization = result.POSITION.data.quantization;
  1281. var range = quantization.range;
  1282. pointCloud._quantizedVolumeScale = Cartesian3.fromElements(
  1283. range,
  1284. range,
  1285. range
  1286. );
  1287. pointCloud._quantizedVolumeOffset = Cartesian3.unpack(
  1288. quantization.minValues
  1289. );
  1290. pointCloud._quantizedRange =
  1291. (1 << quantization.quantizationBits) - 1.0;
  1292. pointCloud._isQuantizedDraco = true;
  1293. }
  1294. if (isOctEncodedDraco) {
  1295. pointCloud._octEncodedRange =
  1296. (1 << result.NORMAL.data.quantization.quantizationBits) - 1.0;
  1297. pointCloud._isOctEncodedDraco = true;
  1298. }
  1299. var styleableProperties = parsedContent.styleableProperties;
  1300. var batchTableProperties = draco.batchTableProperties;
  1301. for (var name in batchTableProperties) {
  1302. if (batchTableProperties.hasOwnProperty(name)) {
  1303. var property = result[name];
  1304. if (!defined(styleableProperties)) {
  1305. styleableProperties = {};
  1306. }
  1307. styleableProperties[name] = {
  1308. typedArray: property.array,
  1309. componentCount: property.data.componentsPerAttribute,
  1310. };
  1311. }
  1312. }
  1313. parsedContent.positions = defaultValue(
  1314. decodedPositions,
  1315. parsedContent.positions
  1316. );
  1317. parsedContent.colors = defaultValue(
  1318. defaultValue(decodedRgba, decodedRgb),
  1319. parsedContent.colors
  1320. );
  1321. parsedContent.normals = defaultValue(
  1322. decodedNormals,
  1323. parsedContent.normals
  1324. );
  1325. parsedContent.batchIds = defaultValue(
  1326. decodedBatchIds,
  1327. parsedContent.batchIds
  1328. );
  1329. parsedContent.styleableProperties = styleableProperties;
  1330. })
  1331. .otherwise(function (error) {
  1332. pointCloud._decodingState = DecodingState.FAILED;
  1333. pointCloud._readyPromise.reject(error);
  1334. });
  1335. }
  1336. }
  1337. return true;
  1338. }
  1339. var scratchComputedTranslation = new Cartesian4();
  1340. var scratchScale = new Cartesian3();
  1341. PointCloud.prototype.update = function (frameState) {
  1342. var context = frameState.context;
  1343. var decoding = decodeDraco(this, context);
  1344. if (decoding) {
  1345. return;
  1346. }
  1347. var shadersDirty = false;
  1348. var modelMatrixDirty = !Matrix4.equals(this._modelMatrix, this.modelMatrix);
  1349. if (this._mode !== frameState.mode) {
  1350. this._mode = frameState.mode;
  1351. modelMatrixDirty = true;
  1352. }
  1353. if (!defined(this._drawCommand)) {
  1354. createResources(this, frameState);
  1355. modelMatrixDirty = true;
  1356. shadersDirty = true;
  1357. this._ready = true;
  1358. this._readyPromise.resolve(this);
  1359. this._parsedContent = undefined; // Unload
  1360. }
  1361. if (modelMatrixDirty) {
  1362. Matrix4.clone(this.modelMatrix, this._modelMatrix);
  1363. var modelMatrix = this._drawCommand.modelMatrix;
  1364. Matrix4.clone(this._modelMatrix, modelMatrix);
  1365. if (defined(this._rtcCenter)) {
  1366. Matrix4.multiplyByTranslation(modelMatrix, this._rtcCenter, modelMatrix);
  1367. }
  1368. if (defined(this._quantizedVolumeOffset)) {
  1369. Matrix4.multiplyByTranslation(
  1370. modelMatrix,
  1371. this._quantizedVolumeOffset,
  1372. modelMatrix
  1373. );
  1374. }
  1375. if (frameState.mode !== SceneMode.SCENE3D) {
  1376. var projection = frameState.mapProjection;
  1377. var translation = Matrix4.getColumn(
  1378. modelMatrix,
  1379. 3,
  1380. scratchComputedTranslation
  1381. );
  1382. if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {
  1383. Transforms.basisTo2D(projection, modelMatrix, modelMatrix);
  1384. }
  1385. }
  1386. var boundingSphere = this._drawCommand.boundingVolume;
  1387. BoundingSphere.clone(this._boundingSphere, boundingSphere);
  1388. if (this._cull) {
  1389. var center = boundingSphere.center;
  1390. Matrix4.multiplyByPoint(modelMatrix, center, center);
  1391. var scale = Matrix4.getScale(modelMatrix, scratchScale);
  1392. boundingSphere.radius *= Cartesian3.maximumComponent(scale);
  1393. }
  1394. }
  1395. if (this.clippingPlanesDirty) {
  1396. this.clippingPlanesDirty = false;
  1397. shadersDirty = true;
  1398. }
  1399. if (this._attenuation !== this.attenuation) {
  1400. this._attenuation = this.attenuation;
  1401. shadersDirty = true;
  1402. }
  1403. if (this.backFaceCulling !== this._backFaceCulling) {
  1404. this._backFaceCulling = this.backFaceCulling;
  1405. shadersDirty = true;
  1406. }
  1407. if (this.normalShading !== this._normalShading) {
  1408. this._normalShading = this.normalShading;
  1409. shadersDirty = true;
  1410. }
  1411. if (this._style !== this.style || this.styleDirty) {
  1412. this._style = this.style;
  1413. this.styleDirty = false;
  1414. shadersDirty = true;
  1415. }
  1416. if (shadersDirty) {
  1417. createShaders(this, frameState, this._style);
  1418. }
  1419. this._drawCommand.castShadows = ShadowMode.castShadows(this.shadows);
  1420. this._drawCommand.receiveShadows = ShadowMode.receiveShadows(this.shadows);
  1421. // Update the render state
  1422. var isTranslucent =
  1423. this._highlightColor.alpha < 1.0 ||
  1424. this._constantColor.alpha < 1.0 ||
  1425. this._styleTranslucent;
  1426. this._drawCommand.renderState = isTranslucent
  1427. ? this._translucentRenderState
  1428. : this._opaqueRenderState;
  1429. this._drawCommand.pass = isTranslucent ? Pass.TRANSLUCENT : this._opaquePass;
  1430. var commandList = frameState.commandList;
  1431. var passes = frameState.passes;
  1432. if (passes.render || passes.pick) {
  1433. commandList.push(this._drawCommand);
  1434. }
  1435. };
  1436. PointCloud.prototype.isDestroyed = function () {
  1437. return false;
  1438. };
  1439. PointCloud.prototype.destroy = function () {
  1440. var command = this._drawCommand;
  1441. if (defined(command)) {
  1442. command.vertexArray = command.vertexArray && command.vertexArray.destroy();
  1443. command.shaderProgram =
  1444. command.shaderProgram && command.shaderProgram.destroy();
  1445. }
  1446. return destroyObject(this);
  1447. };
  1448. export default PointCloud;