PointCloud.js 44 KB

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