| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 | 
							- import AttributeCompression from "../../Core/AttributeCompression.js";
 
- import Cartesian3 from "../../Core/Cartesian3.js";
 
- import Color from "../../Core/Color.js";
 
- import Check from "../../Core/Check.js";
 
- import ComponentDatatype from "../../Core/ComponentDatatype.js";
 
- import defaultValue from "../../Core/defaultValue.js";
 
- import defer from "../../Core/defer.js";
 
- import defined from "../../Core/defined.js";
 
- import Matrix4 from "../../Core/Matrix4.js";
 
- import PrimitiveType from "../../Core/PrimitiveType.js";
 
- import MersenneTwister from "../../ThirdParty/mersenne-twister.js";
 
- import Buffer from "../../Renderer/Buffer.js";
 
- import BufferUsage from "../../Renderer/BufferUsage.js";
 
- import AlphaMode from "../AlphaMode.js";
 
- import AttributeType from "../AttributeType.js";
 
- import Axis from "../Axis.js";
 
- import parseBatchTable from "../parseBatchTable.js";
 
- import DracoLoader from "../DracoLoader.js";
 
- import StructuralMetadata from "../StructuralMetadata.js";
 
- import ResourceLoader from "../ResourceLoader.js";
 
- import MetadataClass from "../MetadataClass.js";
 
- import ModelComponents from "../ModelComponents.js";
 
- import PntsParser from "../PntsParser.js";
 
- import PropertyTable from "../PropertyTable.js";
 
- import ResourceLoaderState from "../ResourceLoaderState.js";
 
- import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
 
- const Components = ModelComponents.Components;
 
- const Scene = ModelComponents.Scene;
 
- const Node = ModelComponents.Node;
 
- const Primitive = ModelComponents.Primitive;
 
- const Attribute = ModelComponents.Attribute;
 
- const Quantization = ModelComponents.Quantization;
 
- const FeatureIdAttribute = ModelComponents.FeatureIdAttribute;
 
- const Material = ModelComponents.Material;
 
- const MetallicRoughness = ModelComponents.MetallicRoughness;
 
- /**
 
-  * Loads a .pnts point cloud and transcodes it into a {@link ModelComponents}
 
-  *
 
-  * @alias PntsLoader
 
-  * @constructor
 
-  * @augments ResourceLoader
 
-  * @private
 
-  *
 
-  * @param {Object} options An object containing the following properties
 
-  * @param {ArrayBuffer} options.arrayBuffer The array buffer of the pnts contents
 
-  * @param {Number} [options.byteOffset] The byte offset to the beginning of the pnts contents in the array buffer
 
-  */
 
- export default function PntsLoader(options) {
 
-   options = defaultValue(options, defaultValue.EMPTY_OBJECT);
 
-   const arrayBuffer = options.arrayBuffer;
 
-   const byteOffset = defaultValue(options.byteOffset, 0);
 
-   //>>includeStart('debug', pragmas.debug);
 
-   Check.typeOf.object("options.arrayBuffer", arrayBuffer);
 
-   //>>includeEnd('debug');
 
-   this._arrayBuffer = arrayBuffer;
 
-   this._byteOffset = byteOffset;
 
-   this._parsedContent = undefined;
 
-   this._decodePromise = undefined;
 
-   this._decodedAttributes = undefined;
 
-   this._promise = defer();
 
-   this._state = ResourceLoaderState.UNLOADED;
 
-   this._buffers = [];
 
-   // The batch table object contains a json and a binary component access using keys of the same name.
 
-   this._components = undefined;
 
-   this._transform = Matrix4.IDENTITY;
 
- }
 
- if (defined(Object.create)) {
 
-   PntsLoader.prototype = Object.create(ResourceLoader.prototype);
 
-   PntsLoader.prototype.constructor = PntsLoader;
 
- }
 
- Object.defineProperties(PntsLoader.prototype, {
 
-   /**
 
-    * A promise that resolves to the resource when the resource is ready.
 
-    *
 
-    * @memberof PntsLoader.prototype
 
-    *
 
-    * @type {Promise.<PntsLoader>}
 
-    * @readonly
 
-    * @private
 
-    */
 
-   promise: {
 
-     get: function () {
 
-       return this._promise.promise;
 
-     },
 
-   },
 
-   /**
 
-    * The cache key of the resource
 
-    *
 
-    * @memberof PntsLoader.prototype
 
-    *
 
-    * @type {String}
 
-    * @readonly
 
-    * @private
 
-    */
 
-   cacheKey: {
 
-     get: function () {
 
-       return undefined;
 
-     },
 
-   },
 
-   /**
 
-    * The loaded components.
 
-    *
 
-    * @memberof PntsLoader.prototype
 
-    *
 
-    * @type {ModelComponents.Components}
 
-    * @readonly
 
-    * @private
 
-    */
 
-   components: {
 
-     get: function () {
 
-       return this._components;
 
-     },
 
-   },
 
-   /**
 
-    * A world-space transform to apply to the primitives.
 
-    * See {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/PointCloud#global-semantics}
 
-    *
 
-    * @memberof PntsLoader.prototype
 
-    *
 
-    * @type {Matrix4}
 
-    * @readonly
 
-    * @private
 
-    */
 
-   transform: {
 
-     get: function () {
 
-       return this._transform;
 
-     },
 
-   },
 
- });
 
- /**
 
-  * Loads the resource.
 
-  * @private
 
-  */
 
- PntsLoader.prototype.load = function () {
 
-   this._parsedContent = PntsParser.parse(this._arrayBuffer, this._byteOffset);
 
-   this._state = ResourceLoaderState.PROCESSING;
 
- };
 
- PntsLoader.prototype.process = function (frameState) {
 
-   if (this._state === ResourceLoaderState.PROCESSING) {
 
-     if (!defined(this._decodePromise)) {
 
-       decodeDraco(this, frameState.context);
 
-     }
 
-   }
 
- };
 
- function decodeDraco(loader, context) {
 
-   const parsedContent = loader._parsedContent;
 
-   const draco = parsedContent.draco;
 
-   let decodePromise;
 
-   if (!defined(draco)) {
 
-     // The draco extension wasn't present,
 
-     decodePromise = Promise.resolve();
 
-   } else {
 
-     decodePromise = DracoLoader.decodePointCloud(draco, context);
 
-   }
 
-   if (!defined(decodePromise)) {
 
-     // Could not schedule Draco decoding this frame.
 
-     return;
 
-   }
 
-   loader._decodePromise = decodePromise;
 
-   decodePromise
 
-     .then(function (decodeDracoResult) {
 
-       if (loader.isDestroyed()) {
 
-         return;
 
-       }
 
-       if (defined(decodeDracoResult)) {
 
-         processDracoAttributes(loader, draco, decodeDracoResult);
 
-       }
 
-       makeComponents(loader, context);
 
-       loader._state = ResourceLoaderState.READY;
 
-       loader._promise.resolve(loader);
 
-     })
 
-     .catch(function (error) {
 
-       loader.unload();
 
-       loader._state = ResourceLoaderState.FAILED;
 
-       const errorMessage = "Failed to load Draco";
 
-       loader._promise.reject(loader.getError(errorMessage, error));
 
-     });
 
- }
 
- function processDracoAttributes(loader, draco, result) {
 
-   loader._state = ResourceLoaderState.READY;
 
-   const parsedContent = loader._parsedContent;
 
-   let attribute;
 
-   if (defined(result.POSITION)) {
 
-     attribute = {
 
-       name: "POSITION",
 
-       semantic: VertexAttributeSemantic.POSITION,
 
-       typedArray: result.POSITION.array,
 
-       componentDatatype: ComponentDatatype.FLOAT,
 
-       type: AttributeType.VEC3,
 
-       isQuantized: false,
 
-     };
 
-     if (defined(result.POSITION.data.quantization)) {
 
-       // Draco quantization range == quantized volume scale - size in meters of the quantized volume
 
-       // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc
 
-       const quantization = result.POSITION.data.quantization;
 
-       const range = quantization.range;
 
-       const quantizedVolumeScale = Cartesian3.fromElements(range, range, range);
 
-       const quantizedVolumeOffset = Cartesian3.unpack(quantization.minValues);
 
-       const quantizedRange = (1 << quantization.quantizationBits) - 1.0;
 
-       attribute.isQuantized = true;
 
-       attribute.quantizedRange = quantizedRange;
 
-       attribute.quantizedVolumeOffset = quantizedVolumeOffset;
 
-       attribute.quantizedVolumeScale = quantizedVolumeScale;
 
-       attribute.quantizedComponentDatatype = ComponentDatatype.UNSIGNED_SHORT;
 
-       attribute.quantizedType = AttributeType.VEC3;
 
-     }
 
-     parsedContent.positions = attribute;
 
-   }
 
-   if (defined(result.NORMAL)) {
 
-     attribute = {
 
-       name: "NORMAL",
 
-       semantic: VertexAttributeSemantic.NORMAL,
 
-       typedArray: result.NORMAL.array,
 
-       componentDatatype: ComponentDatatype.FLOAT,
 
-       type: AttributeType.VEC3,
 
-       isQuantized: false,
 
-       octEncoded: false,
 
-       octEncodedZXY: false,
 
-     };
 
-     if (defined(result.NORMAL.data.quantization)) {
 
-       const octEncodedRange =
 
-         (1 << result.NORMAL.data.quantization.quantizationBits) - 1.0;
 
-       attribute.quantizedRange = octEncodedRange;
 
-       attribute.octEncoded = true;
 
-       attribute.octEncodedZXY = true;
 
-       attribute.quantizedComponentDatatype = ComponentDatatype.UNSIGNED_BYTE;
 
-       attribute.quantizedType = AttributeType.VEC2;
 
-     }
 
-     parsedContent.normals = attribute;
 
-   }
 
-   if (defined(result.RGBA)) {
 
-     parsedContent.colors = {
 
-       name: "COLOR",
 
-       semantic: VertexAttributeSemantic.COLOR,
 
-       setIndex: 0,
 
-       typedArray: result.RGBA.array,
 
-       componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
 
-       type: AttributeType.VEC4,
 
-       normalized: true,
 
-       isTranslucent: true,
 
-     };
 
-   } else if (defined(result.RGB)) {
 
-     parsedContent.colors = {
 
-       name: "COLOR",
 
-       semantic: VertexAttributeSemantic.COLOR,
 
-       setIndex: 0,
 
-       typedArray: result.RGB.array,
 
-       componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
 
-       type: AttributeType.VEC3,
 
-       normalized: true,
 
-       isTranslucent: false,
 
-     };
 
-   }
 
-   // Transcode Batch ID (3D Tiles 1.0) -> Feature ID (3D Tiles Next)
 
-   if (defined(result.BATCH_ID)) {
 
-     const batchIds = result.BATCH_ID.array;
 
-     parsedContent.batchIds = {
 
-       name: "_FEATURE_ID",
 
-       semantic: VertexAttributeSemantic.FEATURE_ID,
 
-       setIndex: 0,
 
-       typedArray: batchIds,
 
-       componentDatatype: ComponentDatatype.fromTypedArray(batchIds),
 
-       type: AttributeType.SCALAR,
 
-     };
 
-   }
 
-   let styleableProperties = parsedContent.styleableProperties;
 
-   const batchTableProperties = draco.batchTableProperties;
 
-   for (const name in batchTableProperties) {
 
-     if (batchTableProperties.hasOwnProperty(name)) {
 
-       const property = result[name];
 
-       if (!defined(styleableProperties)) {
 
-         styleableProperties = {};
 
-       }
 
-       styleableProperties[name] = {
 
-         typedArray: property.array,
 
-         componentCount: property.data.componentsPerAttribute,
 
-       };
 
-     }
 
-   }
 
-   parsedContent.styleableProperties = styleableProperties;
 
- }
 
- function makeAttribute(loader, attributeInfo, context) {
 
-   let typedArray = attributeInfo.typedArray;
 
-   let quantization;
 
-   if (attributeInfo.octEncoded) {
 
-     quantization = new Quantization();
 
-     quantization.octEncoded = attributeInfo.octEncoded;
 
-     quantization.octEncodedZXY = attributeInfo.octEncodedZXY;
 
-     quantization.normalizationRange = attributeInfo.quantizedRange;
 
-     quantization.type = attributeInfo.quantizedType;
 
-     quantization.componentDatatype = attributeInfo.quantizedComponentDatatype;
 
-   }
 
-   if (attributeInfo.isQuantized) {
 
-     quantization = new Quantization();
 
-     const normalizationRange = attributeInfo.quantizedRange;
 
-     quantization.normalizationRange = normalizationRange;
 
-     // volume offset sometimes requires 64-bit precision so this is handled
 
-     // in the components.transform matrix.
 
-     quantization.quantizedVolumeOffset = Cartesian3.ZERO;
 
-     const quantizedVolumeDimensions = attributeInfo.quantizedVolumeScale;
 
-     quantization.quantizedVolumeDimensions = quantizedVolumeDimensions;
 
-     quantization.quantizedVolumeStepSize = Cartesian3.divideByScalar(
 
-       quantizedVolumeDimensions,
 
-       normalizationRange,
 
-       new Cartesian3()
 
-     );
 
-     quantization.componentDatatype = attributeInfo.quantizedComponentDatatype;
 
-     quantization.type = attributeInfo.quantizedType;
 
-   }
 
-   const attribute = new Attribute();
 
-   attribute.name = attributeInfo.name;
 
-   attribute.semantic = attributeInfo.semantic;
 
-   attribute.setIndex = attributeInfo.setIndex;
 
-   attribute.componentDatatype = attributeInfo.componentDatatype;
 
-   attribute.type = attributeInfo.type;
 
-   attribute.normalized = defaultValue(attributeInfo.normalized, false);
 
-   attribute.min = attributeInfo.min;
 
-   attribute.max = attributeInfo.max;
 
-   attribute.quantization = quantization;
 
-   if (attributeInfo.isRGB565) {
 
-     typedArray = AttributeCompression.decodeRGB565(typedArray);
 
-   }
 
-   if (defined(attributeInfo.constantColor)) {
 
-     const packedColor = new Array(4);
 
-     attribute.constant = Color.pack(attributeInfo.constantColor, packedColor);
 
-   } else {
 
-     const buffer = Buffer.createVertexBuffer({
 
-       typedArray: typedArray,
 
-       context: context,
 
-       usage: BufferUsage.STATIC_DRAW,
 
-     });
 
-     buffer.vertexArrayDestroyable = false;
 
-     loader._buffers.push(buffer);
 
-     attribute.buffer = buffer;
 
-   }
 
-   return attribute;
 
- }
 
- let randomNumberGenerator;
 
- let randomValues;
 
- function getRandomValues(samplesLength) {
 
-   // Use same random values across all runs
 
-   if (!defined(randomValues)) {
 
-     // Use MersenneTwister directly to avoid interfering with CesiumMath.nextRandomNumber()
 
-     // See https://github.com/CesiumGS/cesium/issues/9730
 
-     randomNumberGenerator = new MersenneTwister(0);
 
-     randomValues = new Array(samplesLength);
 
-     for (let i = 0; i < samplesLength; ++i) {
 
-       randomValues[i] = randomNumberGenerator.random();
 
-     }
 
-   }
 
-   return randomValues;
 
- }
 
- const scratchMin = new Cartesian3();
 
- const scratchMax = new Cartesian3();
 
- const scratchPosition = new Cartesian3();
 
- function computeApproximateExtrema(positions) {
 
-   const positionsArray = positions.typedArray;
 
-   const maximumSamplesLength = 20;
 
-   const pointsLength = positionsArray.length / 3;
 
-   const samplesLength = Math.min(pointsLength, maximumSamplesLength);
 
-   const randomValues = getRandomValues(maximumSamplesLength);
 
-   const maxValue = Number.MAX_VALUE;
 
-   const minValue = -Number.MAX_VALUE;
 
-   let min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin);
 
-   let max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax);
 
-   let i;
 
-   let index;
 
-   let position;
 
-   if (positions.isQuantized) {
 
-     // The quantized volume offset is not used here since it will become part of
 
-     // the model matrix.
 
-     min = Cartesian3.ZERO;
 
-     max = positions.quantizedVolumeScale;
 
-   } else {
 
-     for (i = 0; i < samplesLength; ++i) {
 
-       index = Math.floor(randomValues[i] * pointsLength);
 
-       position = Cartesian3.unpack(positionsArray, index * 3, scratchPosition);
 
-       Cartesian3.minimumByComponent(min, position, min);
 
-       Cartesian3.maximumByComponent(max, position, max);
 
-     }
 
-   }
 
-   positions.min = Cartesian3.clone(min);
 
-   positions.max = Cartesian3.clone(max);
 
- }
 
- // By default, point clouds are rendered as dark gray.
 
- const defaultColorAttribute = {
 
-   name: VertexAttributeSemantic.COLOR,
 
-   semantic: VertexAttributeSemantic.COLOR,
 
-   setIndex: 0,
 
-   constantColor: Color.DARKGRAY,
 
-   componentDatatype: ComponentDatatype.FLOAT,
 
-   type: AttributeType.VEC4,
 
-   isQuantized: false,
 
-   isTranslucent: false,
 
- };
 
- function makeAttributes(loader, parsedContent, context) {
 
-   const attributes = [];
 
-   let attribute;
 
-   const positions = parsedContent.positions;
 
-   if (defined(positions)) {
 
-     computeApproximateExtrema(positions);
 
-     attribute = makeAttribute(loader, positions, context);
 
-     attribute.count = parsedContent.pointsLength;
 
-     attributes.push(attribute);
 
-   }
 
-   if (defined(parsedContent.normals)) {
 
-     attribute = makeAttribute(loader, parsedContent.normals, context);
 
-     attributes.push(attribute);
 
-   }
 
-   if (defined(parsedContent.colors)) {
 
-     attribute = makeAttribute(loader, parsedContent.colors, context);
 
-     attributes.push(attribute);
 
-   } else {
 
-     attribute = makeAttribute(loader, defaultColorAttribute, context);
 
-     attributes.push(attribute);
 
-   }
 
-   if (defined(parsedContent.batchIds)) {
 
-     attribute = makeAttribute(loader, parsedContent.batchIds, context);
 
-     attributes.push(attribute);
 
-   }
 
-   return attributes;
 
- }
 
- function makeStructuralMetadata(parsedContent) {
 
-   const batchLength = parsedContent.batchLength;
 
-   const pointsLength = parsedContent.pointsLength;
 
-   const batchTableBinary = parsedContent.batchTableBinary;
 
-   if (defined(batchTableBinary)) {
 
-     const count = defaultValue(batchLength, pointsLength);
 
-     return parseBatchTable({
 
-       count: count,
 
-       batchTable: parsedContent.batchTableJson,
 
-       binaryBody: batchTableBinary,
 
-     });
 
-   }
 
-   // If batch table is not defined, create a property table without any properties.
 
-   const emptyPropertyTable = new PropertyTable({
 
-     name: MetadataClass.BATCH_TABLE_CLASS_NAME,
 
-     count: pointsLength,
 
-   });
 
-   return new StructuralMetadata({
 
-     schema: {},
 
-     propertyTables: [emptyPropertyTable],
 
-   });
 
- }
 
- function makeComponents(loader, context) {
 
-   const parsedContent = loader._parsedContent;
 
-   const metallicRoughness = new MetallicRoughness();
 
-   metallicRoughness.metallicFactor = 0;
 
-   metallicRoughness.roughnessFactor = 0.9;
 
-   const material = new Material();
 
-   material.metallicRoughness = metallicRoughness;
 
-   const colors = parsedContent.colors;
 
-   if (defined(colors) && colors.isTranslucent) {
 
-     material.alphaMode = AlphaMode.BLEND;
 
-   }
 
-   // Render point clouds as unlit, unless normals are present, in which case
 
-   // render as a PBR material.
 
-   const isUnlit = !defined(parsedContent.normals);
 
-   material.unlit = isUnlit;
 
-   const primitive = new Primitive();
 
-   primitive.attributes = makeAttributes(loader, parsedContent, context);
 
-   primitive.primitiveType = PrimitiveType.POINTS;
 
-   primitive.material = material;
 
-   if (defined(parsedContent.batchIds)) {
 
-     const featureIdAttribute = new FeatureIdAttribute();
 
-     featureIdAttribute.propertyTableId = 0;
 
-     featureIdAttribute.setIndex = 0;
 
-     featureIdAttribute.positionalLabel = "featureId_0";
 
-     primitive.featureIds.push(featureIdAttribute);
 
-   }
 
-   const node = new Node();
 
-   node.index = 0;
 
-   node.primitives = [primitive];
 
-   const scene = new Scene();
 
-   scene.nodes = [node];
 
-   scene.upAxis = Axis.Z;
 
-   scene.forwardAxis = Axis.X;
 
-   const components = new Components();
 
-   components.scene = scene;
 
-   components.nodes = [node];
 
-   components.structuralMetadata = makeStructuralMetadata(parsedContent);
 
-   if (defined(parsedContent.rtcCenter)) {
 
-     components.transform = Matrix4.multiplyByTranslation(
 
-       components.transform,
 
-       parsedContent.rtcCenter,
 
-       components.transform
 
-     );
 
-   }
 
-   const positions = parsedContent.positions;
 
-   if (defined(positions) && positions.isQuantized) {
 
-     // The volume offset is sometimes in ECEF, so this is applied here rather
 
-     // than the dequantization shader to avoid jitter
 
-     components.transform = Matrix4.multiplyByTranslation(
 
-       components.transform,
 
-       positions.quantizedVolumeOffset,
 
-       components.transform
 
-     );
 
-   }
 
-   loader._components = components;
 
-   // Free the parsed content so we don't hold onto the large typed arrays.
 
-   loader._parsedContent = undefined;
 
- }
 
- PntsLoader.prototype.unload = function () {
 
-   const buffers = this._buffers;
 
-   for (let i = 0; i < buffers.length; i++) {
 
-     buffers[i].destroy();
 
-   }
 
-   buffers.length = 0;
 
-   this._components = undefined;
 
-   this._parsedContent = undefined;
 
- };
 
 
  |