PointCloud.js 44 KB

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