text: import arraySlice from "../Core/arraySlice.js";
  import BoundingSphere from "../Core/BoundingSphere.js";
  import Cartesian3 from "../Core/Cartesian3.js";
  import Cartesian4 from "../Core/Cartesian4.js";
  import Check from "../Core/Check.js";
  import clone from "../Core/clone.js";
  import Color from "../Core/Color.js";
  import combine from "../Core/combine.js";
  import ComponentDatatype from "../Core/ComponentDatatype.js";
  import defaultValue from "../Core/defaultValue.js";
  import defined from "../Core/defined.js";
  import destroyObject from "../Core/destroyObject.js";
  import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";
  import CesiumMath from "../Core/Math.js";
  import Matrix4 from "../Core/Matrix4.js";
  import oneTimeWarning from "../Core/oneTimeWarning.js";
  import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  import PrimitiveType from "../Core/PrimitiveType.js";
  import RuntimeError from "../Core/RuntimeError.js";
  import Transforms from "../Core/Transforms.js";
  import Buffer from "../Renderer/Buffer.js";
  import BufferUsage from "../Renderer/BufferUsage.js";
  import DrawCommand from "../Renderer/DrawCommand.js";
  import Pass from "../Renderer/Pass.js";
  import RenderState from "../Renderer/RenderState.js";
  import ShaderProgram from "../Renderer/ShaderProgram.js";
  import VertexArray from "../Renderer/VertexArray.js";
  import when from "../ThirdParty/when.js";
  import BlendingState from "./BlendingState.js";
  import Cesium3DTileBatchTable from "./Cesium3DTileBatchTable.js";
  import Cesium3DTileFeatureTable from "./Cesium3DTileFeatureTable.js";
  import DracoLoader from "./DracoLoader.js";
  import getClipAndStyleCode from "./getClipAndStyleCode.js";
  import getClippingFunction from "./getClippingFunction.js";
  import SceneMode from "./SceneMode.js";
  import ShadowMode from "./ShadowMode.js";
  import StencilConstants from "./StencilConstants.js";
  
  var DecodingState = {
    NEEDS_DECODE: 0,
    DECODING: 1,
    READY: 2,
    FAILED: 3,
  };
  
  /**
   * Represents the contents of a
   * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/PointCloud|Point Cloud}
   * tile. Used internally by {@link PointCloud3DTileContent} and {@link TimeDynamicPointCloud}.
   *
   * @alias PointCloud
   * @constructor
   *
   * @see PointCloud3DTileContent
   * @see TimeDynamicPointCloud
   *
   * @private
   */
  function PointCloud(options) {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object("options", options);
    Check.typeOf.object("options.arrayBuffer", options.arrayBuffer);
    //>>includeEnd('debug');
  
    // Hold onto the payload until the render resources are created
    this._parsedContent = undefined;
  
    this._drawCommand = undefined;
    this._isTranslucent = false;
    this._styleTranslucent = false;
    this._constantColor = Color.clone(Color.DARKGRAY);
    this._highlightColor = Color.clone(Color.WHITE);
    this._pointSize = 1.0;
  
    this._rtcCenter = undefined;
    this._quantizedVolumeScale = undefined;
    this._quantizedVolumeOffset = undefined;
  
    // These values are used to regenerate the shader when the style changes
    this._styleableShaderAttributes = undefined;
    this._isQuantized = false;
    this._isOctEncoded16P = false;
    this._isRGB565 = false;
    this._hasColors = false;
    this._hasNormals = false;
    this._hasBatchIds = false;
  
    // Draco
    this._decodingState = DecodingState.READY;
    this._dequantizeInShader = true;
    this._isQuantizedDraco = false;
    this._isOctEncodedDraco = false;
    this._quantizedRange = 0.0;
    this._octEncodedRange = 0.0;
  
    // Use per-point normals to hide back-facing points.
    this.backFaceCulling = false;
    this._backFaceCulling = false;
  
    // Whether to enable normal shading
    this.normalShading = true;
    this._normalShading = true;
  
    this._opaqueRenderState = undefined;
    this._translucentRenderState = undefined;
  
    this._mode = undefined;
  
    this._ready = false;
    this._readyPromise = when.defer();
    this._pointsLength = 0;
    this._geometryByteLength = 0;
  
    this._vertexShaderLoaded = options.vertexShaderLoaded;
    this._fragmentShaderLoaded = options.fragmentShaderLoaded;
    this._uniformMapLoaded = options.uniformMapLoaded;
    this._batchTableLoaded = options.batchTableLoaded;
    this._pickIdLoaded = options.pickIdLoaded;
    this._opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
    this._cull = defaultValue(options.cull, true);
  
    this.style = undefined;
    this._style = undefined;
    this.styleDirty = false;
  
    this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
    this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  
    this.time = 0.0; // For styling
    this.shadows = ShadowMode.ENABLED;
    this._boundingSphere = undefined;
  
    this.clippingPlanes = undefined;
    this.isClipped = false;
    this.clippingPlanesDirty = false;
    // If defined, use this matrix to position the clipping planes instead of the modelMatrix.
    // This is so that when point clouds are part of a tileset they all get clipped relative
    // to the root tile.
    this.clippingPlanesOriginMatrix = undefined;
  
    this.attenuation = false;
    this._attenuation = false;
  
    // Options for geometric error based attenuation
    this.geometricError = 0.0;
    this.geometricErrorScale = 1.0;
    this.maximumAttenuation = this._pointSize;
  
    initialize(this, options);
  }
  
  Object.defineProperties(PointCloud.prototype, {
    pointsLength: {
      get: function () {
        return this._pointsLength;
      },
    },
  
    geometryByteLength: {
      get: function () {
        return this._geometryByteLength;
      },
    },
  
    ready: {
      get: function () {
        return this._ready;
      },
    },
  
    readyPromise: {
      get: function () {
        return this._readyPromise.promise;
      },
    },
  
    color: {
      get: function () {
        return Color.clone(this._highlightColor);
      },
      set: function (value) {
        this._highlightColor = Color.clone(value, this._highlightColor);
      },
    },
  
    boundingSphere: {
      get: function () {
        if (defined(this._drawCommand)) {
          return this._drawCommand.boundingVolume;
        }
        return undefined;
      },
      set: function (value) {
        this._boundingSphere = BoundingSphere.clone(value, this._boundingSphere);
      },
    },
  });
  
  var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  
  function initialize(pointCloud, options) {
    var arrayBuffer = options.arrayBuffer;
    var byteOffset = defaultValue(options.byteOffset, 0);
  
    var uint8Array = new Uint8Array(arrayBuffer);
    var view = new DataView(arrayBuffer);
    byteOffset += sizeOfUint32; // Skip magic
  
    var version = view.getUint32(byteOffset, true);
    if (version !== 1) {
      throw new RuntimeError(
        "Only Point Cloud tile version 1 is supported.  Version " +
          version +
          " is not."
      );
    }
    byteOffset += sizeOfUint32;
  
    // Skip byteLength
    byteOffset += sizeOfUint32;
  
    var featureTableJsonByteLength = view.getUint32(byteOffset, true);
    if (featureTableJsonByteLength === 0) {
      throw new RuntimeError(
        "Feature table must have a byte length greater than zero"
      );
    }
    byteOffset += sizeOfUint32;
  
    var featureTableBinaryByteLength = view.getUint32(byteOffset, true);
    byteOffset += sizeOfUint32;
  
    var batchTableJsonByteLength = view.getUint32(byteOffset, true);
    byteOffset += sizeOfUint32;
    var batchTableBinaryByteLength = view.getUint32(byteOffset, true);
    byteOffset += sizeOfUint32;
  
    var featureTableJson = getJsonFromTypedArray(
      uint8Array,
      byteOffset,
      featureTableJsonByteLength
    );
    byteOffset += featureTableJsonByteLength;
  
    var featureTableBinary = new Uint8Array(
      arrayBuffer,
      byteOffset,
      featureTableBinaryByteLength
    );
    byteOffset += featureTableBinaryByteLength;
  
    // Get the batch table JSON and binary
    var batchTableJson;
    var batchTableBinary;
    if (batchTableJsonByteLength > 0) {
      // Has a batch table JSON
      batchTableJson = getJsonFromTypedArray(
        uint8Array,
        byteOffset,
        batchTableJsonByteLength
      );
      byteOffset += batchTableJsonByteLength;
  
      if (batchTableBinaryByteLength > 0) {
        // Has a batch table binary
        batchTableBinary = new Uint8Array(
          arrayBuffer,
          byteOffset,
          batchTableBinaryByteLength
        );
        byteOffset += batchTableBinaryByteLength;
      }
    }
  
    var featureTable = new Cesium3DTileFeatureTable(
      featureTableJson,
      featureTableBinary
    );
  
    var pointsLength = featureTable.getGlobalProperty("POINTS_LENGTH");
    featureTable.featuresLength = pointsLength;
  
    if (!defined(pointsLength)) {
      throw new RuntimeError(
        "Feature table global property: POINTS_LENGTH must be defined"
      );
    }
  
    var rtcCenter = featureTable.getGlobalProperty(
      "RTC_CENTER",
      ComponentDatatype.FLOAT,
      3
    );
    if (defined(rtcCenter)) {
      pointCloud._rtcCenter = Cartesian3.unpack(rtcCenter);
    }
  
    var positions;
    var colors;
    var normals;
    var batchIds;
  
    var hasPositions = false;
    var hasColors = false;
    var hasNormals = false;
    var hasBatchIds = false;
  
    var isQuantized = false;
    var isTranslucent = false;
    var isRGB565 = false;
    var isOctEncoded16P = false;
  
    var dracoBuffer;
    var dracoFeatureTableProperties;
    var dracoBatchTableProperties;
  
    var featureTableDraco = defined(featureTableJson.extensions)
      ? featureTableJson.extensions["3DTILES_draco_point_compression"]
      : undefined;
    var batchTableDraco =
      defined(batchTableJson) && defined(batchTableJson.extensions)
        ? batchTableJson.extensions["3DTILES_draco_point_compression"]
        : undefined;
  
    if (defined(batchTableDraco)) {
      dracoBatchTableProperties = batchTableDraco.properties;
    }
  
    if (defined(featureTableDraco)) {
      dracoFeatureTableProperties = featureTableDraco.properties;
      var dracoByteOffset = featureTableDraco.byteOffset;
      var dracoByteLength = featureTableDraco.byteLength;
      if (
        !defined(dracoFeatureTableProperties) ||
        !defined(dracoByteOffset) ||
        !defined(dracoByteLength)
      ) {
        throw new RuntimeError(
          "Draco properties, byteOffset, and byteLength must be defined"
        );
      }
      dracoBuffer = arraySlice(
        featureTableBinary,
        dracoByteOffset,
        dracoByteOffset + dracoByteLength
      );
      hasPositions = defined(dracoFeatureTableProperties.POSITION);
      hasColors =
        defined(dracoFeatureTableProperties.RGB) ||
        defined(dracoFeatureTableProperties.RGBA);
      hasNormals = defined(dracoFeatureTableProperties.NORMAL);
      hasBatchIds = defined(dracoFeatureTableProperties.BATCH_ID);
      isTranslucent = defined(dracoFeatureTableProperties.RGBA);
      pointCloud._decodingState = DecodingState.NEEDS_DECODE;
    }
  
    var draco;
    if (defined(dracoBuffer)) {
      draco = {
        buffer: dracoBuffer,
        featureTableProperties: dracoFeatureTableProperties,
        batchTableProperties: dracoBatchTableProperties,
        properties: combine(
          dracoFeatureTableProperties,
          dracoBatchTableProperties
        ),
        dequantizeInShader: pointCloud._dequantizeInShader,
      };
    }
  
    if (!hasPositions) {
      if (defined(featureTableJson.POSITION)) {
        positions = featureTable.getPropertyArray(
          "POSITION",
          ComponentDatatype.FLOAT,
          3
        );
        hasPositions = true;
      } else if (defined(featureTableJson.POSITION_QUANTIZED)) {
        positions = featureTable.getPropertyArray(
          "POSITION_QUANTIZED",
          ComponentDatatype.UNSIGNED_SHORT,
          3
        );
        isQuantized = true;
        hasPositions = true;
  
        var quantizedVolumeScale = featureTable.getGlobalProperty(
          "QUANTIZED_VOLUME_SCALE",
          ComponentDatatype.FLOAT,
          3
        );
        if (!defined(quantizedVolumeScale)) {
          throw new RuntimeError(
            "Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions."
          );
        }
        pointCloud._quantizedVolumeScale = Cartesian3.unpack(
          quantizedVolumeScale
        );
        pointCloud._quantizedRange = (1 << 16) - 1;
  
        var quantizedVolumeOffset = featureTable.getGlobalProperty(
          "QUANTIZED_VOLUME_OFFSET",
          ComponentDatatype.FLOAT,
          3
        );
        if (!defined(quantizedVolumeOffset)) {
          throw new RuntimeError(
            "Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions."
          );
        }
        pointCloud._quantizedVolumeOffset = Cartesian3.unpack(
          quantizedVolumeOffset
        );
      }
    }
  
    if (!hasColors) {
      if (defined(featureTableJson.RGBA)) {
        colors = featureTable.getPropertyArray(
          "RGBA",
          ComponentDatatype.UNSIGNED_BYTE,
          4
        );
        isTranslucent = true;
        hasColors = true;
      } else if (defined(featureTableJson.RGB)) {
        colors = featureTable.getPropertyArray(
          "RGB",
          ComponentDatatype.UNSIGNED_BYTE,
          3
        );
        hasColors = true;
      } else if (defined(featureTableJson.RGB565)) {
        colors = featureTable.getPropertyArray(
          "RGB565",
          ComponentDatatype.UNSIGNED_SHORT,
          1
        );
        isRGB565 = true;
        hasColors = true;
      }
    }
  
    if (!hasNormals) {
      if (defined(featureTableJson.NORMAL)) {
        normals = featureTable.getPropertyArray(
          "NORMAL",
          ComponentDatatype.FLOAT,
          3
        );
        hasNormals = true;
      } else if (defined(featureTableJson.NORMAL_OCT16P)) {
        normals = featureTable.getPropertyArray(
          "NORMAL_OCT16P",
          ComponentDatatype.UNSIGNED_BYTE,
          2
        );
        isOctEncoded16P = true;
        hasNormals = true;
      }
    }
  
    if (!hasBatchIds) {
      if (defined(featureTableJson.BATCH_ID)) {
        batchIds = featureTable.getPropertyArray(
          "BATCH_ID",
          ComponentDatatype.UNSIGNED_SHORT,
          1
        );
        hasBatchIds = true;
      }
    }
  
    if (!hasPositions) {
      throw new RuntimeError(
        "Either POSITION or POSITION_QUANTIZED must be defined."
      );
    }
  
    if (defined(featureTableJson.CONSTANT_RGBA)) {
      var constantRGBA = featureTable.getGlobalProperty(
        "CONSTANT_RGBA",
        ComponentDatatype.UNSIGNED_BYTE,
        4
      );
      pointCloud._constantColor = Color.fromBytes(
        constantRGBA[0],
        constantRGBA[1],
        constantRGBA[2],
        constantRGBA[3],
        pointCloud._constantColor
      );
    }
  
    if (hasBatchIds) {
      var batchLength = featureTable.getGlobalProperty("BATCH_LENGTH");
      if (!defined(batchLength)) {
        throw new RuntimeError(
          "Global property: BATCH_LENGTH must be defined when BATCH_ID is defined."
        );
      }
  
      if (defined(batchTableBinary)) {
        // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
        batchTableBinary = new Uint8Array(batchTableBinary);
      }
  
      if (defined(pointCloud._batchTableLoaded)) {
        pointCloud._batchTableLoaded(
          batchLength,
          batchTableJson,
          batchTableBinary
        );
      }
    }
  
    // If points are not batched and there are per-point properties, use these properties for styling purposes
    var styleableProperties;
    if (!hasBatchIds && defined(batchTableBinary)) {
      styleableProperties = Cesium3DTileBatchTable.getBinaryProperties(
        pointsLength,
        batchTableJson,
        batchTableBinary
      );
    }
  
    pointCloud._parsedContent = {
      positions: positions,
      colors: colors,
      normals: normals,
      batchIds: batchIds,
      styleableProperties: styleableProperties,
      draco: draco,
    };
    pointCloud._pointsLength = pointsLength;
    pointCloud._isQuantized = isQuantized;
    pointCloud._isOctEncoded16P = isOctEncoded16P;
    pointCloud._isRGB565 = isRGB565;
    pointCloud._isTranslucent = isTranslucent;
    pointCloud._hasColors = hasColors;
    pointCloud._hasNormals = hasNormals;
    pointCloud._hasBatchIds = hasBatchIds;
  }
  
  var scratchMin = new Cartesian3();
  var scratchMax = new Cartesian3();
  var scratchPosition = new Cartesian3();
  var randomValues;
  
  function getRandomValues(samplesLength) {
    // Use same random values across all runs
    if (!defined(randomValues)) {
      CesiumMath.setRandomNumberSeed(0);
      randomValues = new Array(samplesLength);
      for (var i = 0; i < samplesLength; ++i) {
        randomValues[i] = CesiumMath.nextRandomNumber();
      }
    }
    return randomValues;
  }
  
  function computeApproximateBoundingSphereFromPositions(positions) {
    var maximumSamplesLength = 20;
    var pointsLength = positions.length / 3;
    var samplesLength = Math.min(pointsLength, maximumSamplesLength);
    var randomValues = getRandomValues(maximumSamplesLength);
    var maxValue = Number.MAX_VALUE;
    var minValue = -Number.MAX_VALUE;
    var min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin);
    var max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax);
    for (var i = 0; i < samplesLength; ++i) {
      var index = Math.floor(randomValues[i] * pointsLength);
      var position = Cartesian3.unpack(positions, index * 3, scratchPosition);
      Cartesian3.minimumByComponent(min, position, min);
      Cartesian3.maximumByComponent(max, position, max);
    }
  
    var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
    boundingSphere.radius += CesiumMath.EPSILON2; // To avoid radius of zero
    return boundingSphere;
  }
  
  function prepareVertexAttribute(typedArray, name) {
    // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT.
    var componentDatatype = ComponentDatatype.fromTypedArray(typedArray);
    if (
      componentDatatype === ComponentDatatype.INT ||
      componentDatatype === ComponentDatatype.UNSIGNED_INT ||
      componentDatatype === ComponentDatatype.DOUBLE
    ) {
      oneTimeWarning(
        "Cast pnts property to floats",
        'Point cloud property "' +
          name +
          '" will be casted to a float array because INT, UNSIGNED_INT, and DOUBLE are not valid WebGL vertex attribute types. Some precision may be lost.'
      );
      return new Float32Array(typedArray);
    }
    return typedArray;
  }
  
  var scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier = new Cartesian4();
  var scratchQuantizedVolumeScaleAndOctEncodedRange = new Cartesian4();
  var scratchColor = new Color();
  
  var positionLocation = 0;
  var colorLocation = 1;
  var normalLocation = 2;
  var batchIdLocation = 3;
  var numberOfAttributes = 4;
  
  var scratchClippingPlanesMatrix = new Matrix4();
  var scratchInverseTransposeClippingPlanesMatrix = new Matrix4();
  
  function createResources(pointCloud, frameState) {
    var context = frameState.context;
    var parsedContent = pointCloud._parsedContent;
    var pointsLength = pointCloud._pointsLength;
    var positions = parsedContent.positions;
    var colors = parsedContent.colors;
    var normals = parsedContent.normals;
    var batchIds = parsedContent.batchIds;
    var styleableProperties = parsedContent.styleableProperties;
    var hasStyleableProperties = defined(styleableProperties);
    var isQuantized = pointCloud._isQuantized;
    var isQuantizedDraco = pointCloud._isQuantizedDraco;
    var isOctEncoded16P = pointCloud._isOctEncoded16P;
    var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
    var quantizedRange = pointCloud._quantizedRange;
    var octEncodedRange = pointCloud._octEncodedRange;
    var isRGB565 = pointCloud._isRGB565;
    var isTranslucent = pointCloud._isTranslucent;
    var hasColors = pointCloud._hasColors;
    var hasNormals = pointCloud._hasNormals;
    var hasBatchIds = pointCloud._hasBatchIds;
  
    var componentsPerAttribute;
    var componentDatatype;
  
    var styleableVertexAttributes = [];
    var styleableShaderAttributes = {};
    pointCloud._styleableShaderAttributes = styleableShaderAttributes;
  
    if (hasStyleableProperties) {
      var attributeLocation = numberOfAttributes;
  
      for (var name in styleableProperties) {
        if (styleableProperties.hasOwnProperty(name)) {
          var property = styleableProperties[name];
          var typedArray = prepareVertexAttribute(property.typedArray, name);
          componentsPerAttribute = property.componentCount;
          componentDatatype = ComponentDatatype.fromTypedArray(typedArray);
  
          var vertexBuffer = Buffer.createVertexBuffer({
            context: context,
            typedArray: typedArray,
            usage: BufferUsage.STATIC_DRAW,
          });
  
          pointCloud._geometryByteLength += vertexBuffer.sizeInBytes;
  
          var vertexAttribute = {
            index: attributeLocation,
            vertexBuffer: vertexBuffer,
            componentsPerAttribute: componentsPerAttribute,
            componentDatatype: componentDatatype,
            normalize: false,
            offsetInBytes: 0,
            strideInBytes: 0,
          };
  
          styleableVertexAttributes.push(vertexAttribute);
          styleableShaderAttributes[name] = {
            location: attributeLocation,
            componentCount: componentsPerAttribute,
          };
          ++attributeLocation;
        }
      }
    }
  
    var positionsVertexBuffer = Buffer.createVertexBuffer({
      context: context,
      typedArray: positions,
      usage: BufferUsage.STATIC_DRAW,
    });
    pointCloud._geometryByteLength += positionsVertexBuffer.sizeInBytes;
  
    var colorsVertexBuffer;
    if (hasColors) {
      colorsVertexBuffer = Buffer.createVertexBuffer({
        context: context,
        typedArray: colors,
        usage: BufferUsage.STATIC_DRAW,
      });
      pointCloud._geometryByteLength += colorsVertexBuffer.sizeInBytes;
    }
  
    var normalsVertexBuffer;
    if (hasNormals) {
      normalsVertexBuffer = Buffer.createVertexBuffer({
        context: context,
        typedArray: normals,
        usage: BufferUsage.STATIC_DRAW,
      });
      pointCloud._geometryByteLength += normalsVertexBuffer.sizeInBytes;
    }
  
    var batchIdsVertexBuffer;
    if (hasBatchIds) {
      batchIds = prepareVertexAttribute(batchIds, "batchIds");
      batchIdsVertexBuffer = Buffer.createVertexBuffer({
        context: context,
        typedArray: batchIds,
        usage: BufferUsage.STATIC_DRAW,
      });
      pointCloud._geometryByteLength += batchIdsVertexBuffer.sizeInBytes;
    }
  
    var attributes = [];
  
    if (isQuantized) {
      componentDatatype = ComponentDatatype.UNSIGNED_SHORT;
    } else if (isQuantizedDraco) {
      componentDatatype =
        quantizedRange <= 255
          ? ComponentDatatype.UNSIGNED_BYTE
          : ComponentDatatype.UNSIGNED_SHORT;
    } else {
      componentDatatype = ComponentDatatype.FLOAT;
    }
  
    attributes.push({
      index: positionLocation,
      vertexBuffer: positionsVertexBuffer,
      componentsPerAttribute: 3,
      componentDatatype: componentDatatype,
      normalize: false,
      offsetInBytes: 0,
      strideInBytes: 0,
    });
  
    if (pointCloud._cull) {
      if (isQuantized || isQuantizedDraco) {
        pointCloud._boundingSphere = BoundingSphere.fromCornerPoints(
          Cartesian3.ZERO,
          pointCloud._quantizedVolumeScale
        );
      } else {
        pointCloud._boundingSphere = computeApproximateBoundingSphereFromPositions(
          positions
        );
      }
    }
  
    if (hasColors) {
      if (isRGB565) {
        attributes.push({
          index: colorLocation,
          vertexBuffer: colorsVertexBuffer,
          componentsPerAttribute: 1,
          componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
          normalize: false,
          offsetInBytes: 0,
          strideInBytes: 0,
        });
      } else {
        var colorComponentsPerAttribute = isTranslucent ? 4 : 3;
        attributes.push({
          index: colorLocation,
          vertexBuffer: colorsVertexBuffer,
          componentsPerAttribute: colorComponentsPerAttribute,
          componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
          normalize: true,
          offsetInBytes: 0,
          strideInBytes: 0,
        });
      }
    }
  
    if (hasNormals) {
      if (isOctEncoded16P) {
        componentsPerAttribute = 2;
        componentDatatype = ComponentDatatype.UNSIGNED_BYTE;
      } else if (isOctEncodedDraco) {
        componentsPerAttribute = 2;
        componentDatatype =
          octEncodedRange <= 255
            ? ComponentDatatype.UNSIGNED_BYTE
            : ComponentDatatype.UNSIGNED_SHORT;
      } else {
        componentsPerAttribute = 3;
        componentDatatype = ComponentDatatype.FLOAT;
      }
      attributes.push({
        index: normalLocation,
        vertexBuffer: normalsVertexBuffer,
        componentsPerAttribute: componentsPerAttribute,
        componentDatatype: componentDatatype,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
      });
    }
  
    if (hasBatchIds) {
      attributes.push({
        index: batchIdLocation,
        vertexBuffer: batchIdsVertexBuffer,
        componentsPerAttribute: 1,
        componentDatatype: ComponentDatatype.fromTypedArray(batchIds),
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
      });
    }
  
    if (hasStyleableProperties) {
      attributes = attributes.concat(styleableVertexAttributes);
    }
  
    var vertexArray = new VertexArray({
      context: context,
      attributes: attributes,
    });
  
    var opaqueRenderState = {
      depthTest: {
        enabled: true,
      },
    };
  
    var translucentRenderState = {
      depthTest: {
        enabled: true,
      },
      depthMask: false,
      blending: BlendingState.ALPHA_BLEND,
    };
  
    if (pointCloud._opaquePass === Pass.CESIUM_3D_TILE) {
      opaqueRenderState.stencilTest = StencilConstants.setCesium3DTileBit();
      opaqueRenderState.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
      translucentRenderState.stencilTest = StencilConstants.setCesium3DTileBit();
      translucentRenderState.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
    }
  
    pointCloud._opaqueRenderState = RenderState.fromCache(opaqueRenderState);
    pointCloud._translucentRenderState = RenderState.fromCache(
      translucentRenderState
    );
  
    pointCloud._drawCommand = new DrawCommand({
      boundingVolume: new BoundingSphere(),
      cull: pointCloud._cull,
      modelMatrix: new Matrix4(),
      primitiveType: PrimitiveType.POINTS,
      vertexArray: vertexArray,
      count: pointsLength,
      shaderProgram: undefined, // Updated in createShaders
      uniformMap: undefined, // Updated in createShaders
      renderState: isTranslucent
        ? pointCloud._translucentRenderState
        : pointCloud._opaqueRenderState,
      pass: isTranslucent ? Pass.TRANSLUCENT : pointCloud._opaquePass,
      owner: pointCloud,
      castShadows: false,
      receiveShadows: false,
      pickId: pointCloud._pickIdLoaded(),
    });
  }
  
  function createUniformMap(pointCloud, frameState) {
    var context = frameState.context;
    var isQuantized = pointCloud._isQuantized;
    var isQuantizedDraco = pointCloud._isQuantizedDraco;
    var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
  
    var uniformMap = {
      u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier: function () {
        var scratch = scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier;
        scratch.x = pointCloud._attenuation
          ? pointCloud.maximumAttenuation
          : pointCloud._pointSize;
        scratch.x *= frameState.pixelRatio;
  
        scratch.y = pointCloud.time;
  
        if (pointCloud._attenuation) {
          var frustum = frameState.camera.frustum;
          var depthMultiplier;
          // Attenuation is maximumAttenuation in 2D/ortho
          if (
            frameState.mode === SceneMode.SCENE2D ||
            frustum instanceof OrthographicFrustum
          ) {
            depthMultiplier = Number.POSITIVE_INFINITY;
          } else {
            depthMultiplier =
              context.drawingBufferHeight /
              frameState.camera.frustum.sseDenominator;
          }
  
          scratch.z = pointCloud.geometricError * pointCloud.geometricErrorScale;
          scratch.w = depthMultiplier;
        }
  
        return scratch;
      },
      u_highlightColor: function () {
        return pointCloud._highlightColor;
      },
      u_constantColor: function () {
        return pointCloud._constantColor;
      },
      u_clippingPlanes: function () {
        var clippingPlanes = pointCloud.clippingPlanes;
        var isClipped = pointCloud.isClipped;
        return isClipped ? clippingPlanes.texture : context.defaultTexture;
      },
      u_clippingPlanesEdgeStyle: function () {
        var clippingPlanes = pointCloud.clippingPlanes;
        if (!defined(clippingPlanes)) {
          return Color.TRANSPARENT;
        }
  
        var style = Color.clone(clippingPlanes.edgeColor, scratchColor);
        style.alpha = clippingPlanes.edgeWidth;
        return style;
      },
      u_clippingPlanesMatrix: function () {
        var clippingPlanes = pointCloud.clippingPlanes;
        if (!defined(clippingPlanes)) {
          return Matrix4.IDENTITY;
        }
  
        var clippingPlanesOriginMatrix = defaultValue(
          pointCloud.clippingPlanesOriginMatrix,
          pointCloud._modelMatrix
        );
        Matrix4.multiply(
          context.uniformState.view3D,
          clippingPlanesOriginMatrix,
          scratchClippingPlanesMatrix
        );
        var transform = Matrix4.multiply(
          scratchClippingPlanesMatrix,
          clippingPlanes.modelMatrix,
          scratchClippingPlanesMatrix
        );
  
        return Matrix4.inverseTranspose(
          transform,
          scratchInverseTransposeClippingPlanesMatrix
        );
      },
    };
  
    if (isQuantized || isQuantizedDraco || isOctEncodedDraco) {
      uniformMap = combine(uniformMap, {
        u_quantizedVolumeScaleAndOctEncodedRange: function () {
          var scratch = scratchQuantizedVolumeScaleAndOctEncodedRange;
          if (defined(pointCloud._quantizedVolumeScale)) {
            var scale = Cartesian3.clone(
              pointCloud._quantizedVolumeScale,
              scratch
            );
            Cartesian3.divideByScalar(scale, pointCloud._quantizedRange, scratch);
          }
          scratch.w = pointCloud._octEncodedRange;
          return scratch;
        },
      });
    }
  
    if (defined(pointCloud._uniformMapLoaded)) {
      uniformMap = pointCloud._uniformMapLoaded(uniformMap);
    }
  
    pointCloud._drawCommand.uniformMap = uniformMap;
  }
  
  function getStyleablePropertyIds(source, propertyIds) {
    // Get all the property IDs used by this style
    var regex = /czm_3dtiles_property_(\d+)/g;
    var matches = regex.exec(source);
    while (matches !== null) {
      var id = parseInt(matches[1]);
      if (propertyIds.indexOf(id) === -1) {
        propertyIds.push(id);
      }
      matches = regex.exec(source);
    }
  }
  
  function getBuiltinPropertyNames(source, propertyNames) {
    // Get all the builtin property names used by this style
    var regex = /czm_3dtiles_builtin_property_(\w+)/g;
    var matches = regex.exec(source);
    while (matches !== null) {
      var name = matches[1];
      if (propertyNames.indexOf(name) === -1) {
        propertyNames.push(name);
      }
      matches = regex.exec(source);
    }
  }
  
  function getVertexAttribute(vertexArray, index) {
    var numberOfAttributes = vertexArray.numberOfAttributes;
    for (var i = 0; i < numberOfAttributes; ++i) {
      var attribute = vertexArray.getAttribute(i);
      if (attribute.index === index) {
        return attribute;
      }
    }
  }
  
  var builtinPropertyNameMap = {
    POSITION: "czm_3dtiles_builtin_property_POSITION",
    POSITION_ABSOLUTE: "czm_3dtiles_builtin_property_POSITION_ABSOLUTE",
    COLOR: "czm_3dtiles_builtin_property_COLOR",
    NORMAL: "czm_3dtiles_builtin_property_NORMAL",
  };
  
  function modifyStyleFunction(source) {
    // Edit the function header to accept the point position, color, and normal
    var functionHeader =
      "(" +
      "vec3 czm_3dtiles_builtin_property_POSITION, " +
      "vec3 czm_3dtiles_builtin_property_POSITION_ABSOLUTE, " +
      "vec4 czm_3dtiles_builtin_property_COLOR, " +
      "vec3 czm_3dtiles_builtin_property_NORMAL" +
      ")";
  
    return source.replace("()", functionHeader);
  }
  
  function createShaders(pointCloud, frameState, style) {
    var i;
    var name;
    var attribute;
  
    var context = frameState.context;
    var hasStyle = defined(style);
    var isQuantized = pointCloud._isQuantized;
    var isQuantizedDraco = pointCloud._isQuantizedDraco;
    var isOctEncoded16P = pointCloud._isOctEncoded16P;
    var isOctEncodedDraco = pointCloud._isOctEncodedDraco;
    var isRGB565 = pointCloud._isRGB565;
    var isTranslucent = pointCloud._isTranslucent;
    var hasColors = pointCloud._hasColors;
    var hasNormals = pointCloud._hasNormals;
    var hasBatchIds = pointCloud._hasBatchIds;
    var backFaceCulling = pointCloud._backFaceCulling;
    var normalShading = pointCloud._normalShading;
    var vertexArray = pointCloud._drawCommand.vertexArray;
    var clippingPlanes = pointCloud.clippingPlanes;
    var attenuation = pointCloud._attenuation;
  
    var colorStyleFunction;
    var showStyleFunction;
    var pointSizeStyleFunction;
    var styleTranslucent = isTranslucent;
  
    var propertyNameMap = clone(builtinPropertyNameMap);
    var propertyIdToAttributeMap = {};
    var styleableShaderAttributes = pointCloud._styleableShaderAttributes;
    for (name in styleableShaderAttributes) {
      if (styleableShaderAttributes.hasOwnProperty(name)) {
        attribute = styleableShaderAttributes[name];
        propertyNameMap[name] = "czm_3dtiles_property_" + attribute.location;
        propertyIdToAttributeMap[attribute.location] = attribute;
      }
    }
  
    if (hasStyle) {
      var shaderState = {
        translucent: false,
      };
      colorStyleFunction = style.getColorShaderFunction(
        "getColorFromStyle",
        propertyNameMap,
        shaderState
      );
      showStyleFunction = style.getShowShaderFunction(
        "getShowFromStyle",
        propertyNameMap,
        shaderState
      );
      pointSizeStyleFunction = style.getPointSizeShaderFunction(
        "getPointSizeFromStyle",
        propertyNameMap,
        shaderState
      );
      if (defined(colorStyleFunction) && shaderState.translucent) {
        styleTranslucent = true;
      }
    }
  
    pointCloud._styleTranslucent = styleTranslucent;
  
    var hasColorStyle = defined(colorStyleFunction);
    var hasShowStyle = defined(showStyleFunction);
    var hasPointSizeStyle = defined(pointSizeStyleFunction);
    var hasClippedContent = pointCloud.isClipped;
  
    // Get the properties in use by the style
    var styleablePropertyIds = [];
    var builtinPropertyNames = [];
  
    if (hasColorStyle) {
      getStyleablePropertyIds(colorStyleFunction, styleablePropertyIds);
      getBuiltinPropertyNames(colorStyleFunction, builtinPropertyNames);
      colorStyleFunction = modifyStyleFunction(colorStyleFunction);
    }
    if (hasShowStyle) {
      getStyleablePropertyIds(showStyleFunction, styleablePropertyIds);
      getBuiltinPropertyNames(showStyleFunction, builtinPropertyNames);
      showStyleFunction = modifyStyleFunction(showStyleFunction);
    }
    if (hasPointSizeStyle) {
      getStyleablePropertyIds(pointSizeStyleFunction, styleablePropertyIds);
      getBuiltinPropertyNames(pointSizeStyleFunction, builtinPropertyNames);
      pointSizeStyleFunction = modifyStyleFunction(pointSizeStyleFunction);
    }
  
    var usesColorSemantic = builtinPropertyNames.indexOf("COLOR") >= 0;
    var usesNormalSemantic = builtinPropertyNames.indexOf("NORMAL") >= 0;
  
    if (usesNormalSemantic && !hasNormals) {
      throw new RuntimeError(
        "Style references the NORMAL semantic but the point cloud does not have normals"
      );
    }
  
    // Disable vertex attributes that aren't used in the style, enable attributes that are
    for (name in styleableShaderAttributes) {
      if (styleableShaderAttributes.hasOwnProperty(name)) {
        attribute = styleableShaderAttributes[name];
        var enabled = styleablePropertyIds.indexOf(attribute.location) >= 0;
        var vertexAttribute = getVertexAttribute(vertexArray, attribute.location);
        vertexAttribute.enabled = enabled;
      }
    }
  
    var usesColors = hasColors && (!hasColorStyle || usesColorSemantic);
    if (hasColors) {
      // Disable the color vertex attribute if the color style does not reference the color semantic
      var colorVertexAttribute = getVertexAttribute(vertexArray, colorLocation);
      colorVertexAttribute.enabled = usesColors;
    }
  
    var usesNormals =
      hasNormals && (normalShading || backFaceCulling || usesNormalSemantic);
    if (hasNormals) {
      // Disable the normal vertex attribute if normals are not used
      var normalVertexAttribute = getVertexAttribute(vertexArray, normalLocation);
      normalVertexAttribute.enabled = usesNormals;
    }
  
    var attributeLocations = {
      a_position: positionLocation,
    };
    if (usesColors) {
      attributeLocations.a_color = colorLocation;
    }
    if (usesNormals) {
      attributeLocations.a_normal = normalLocation;
    }
    if (hasBatchIds) {
      attributeLocations.a_batchId = batchIdLocation;
    }
  
    var attributeDeclarations = "";
  
    var length = styleablePropertyIds.length;
    for (i = 0; i < length; ++i) {
      var propertyId = styleablePropertyIds[i];
      attribute = propertyIdToAttributeMap[propertyId];
      var componentCount = attribute.componentCount;
      var attributeName = "czm_3dtiles_property_" + propertyId;
      var attributeType;
      if (componentCount === 1) {
        attributeType = "float";
      } else {
        attributeType = "vec" + componentCount;
      }
  
      attributeDeclarations +=
        "attribute " + attributeType + " " + attributeName + "; \n";
      attributeLocations[attributeName] = attribute.location;
    }
  
    createUniformMap(pointCloud, frameState);
  
    var vs =
      "attribute vec3 a_position; \n" +
      "varying vec4 v_color; \n" +
      "uniform vec4 u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier; \n" +
      "uniform vec4 u_constantColor; \n" +
      "uniform vec4 u_highlightColor; \n";
    vs += "float u_pointSize; \n" + "float u_time; \n";
  
    if (attenuation) {
      vs += "float u_geometricError; \n" + "float u_depthMultiplier; \n";
    }
  
    vs += attributeDeclarations;
  
    if (usesColors) {
      if (isTranslucent) {
        vs += "attribute vec4 a_color; \n";
      } else if (isRGB565) {
        vs +=
          "attribute float a_color; \n" +
          "const float SHIFT_RIGHT_11 = 1.0 / 2048.0; \n" +
          "const float SHIFT_RIGHT_5 = 1.0 / 32.0; \n" +
          "const float SHIFT_LEFT_11 = 2048.0; \n" +
          "const float SHIFT_LEFT_5 = 32.0; \n" +
          "const float NORMALIZE_6 = 1.0 / 64.0; \n" +
          "const float NORMALIZE_5 = 1.0 / 32.0; \n";
      } else {
        vs += "attribute vec3 a_color; \n";
      }
    }
    if (usesNormals) {
      if (isOctEncoded16P || isOctEncodedDraco) {
        vs += "attribute vec2 a_normal; \n";
      } else {
        vs += "attribute vec3 a_normal; \n";
      }
    }
  
    if (hasBatchIds) {
      vs += "attribute float a_batchId; \n";
    }
  
    if (isQuantized || isQuantizedDraco || isOctEncodedDraco) {
      vs += "uniform vec4 u_quantizedVolumeScaleAndOctEncodedRange; \n";
    }
  
    if (hasColorStyle) {
      vs += colorStyleFunction;
    }
  
    if (hasShowStyle) {
      vs += showStyleFunction;
    }
  
    if (hasPointSizeStyle) {
      vs += pointSizeStyleFunction;
    }
  
    vs +=
      "void main() \n" +
      "{ \n" +
      "    u_pointSize = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.x; \n" +
      "    u_time = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.y; \n";
  
    if (attenuation) {
      vs +=
        "    u_geometricError = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.z; \n" +
        "    u_depthMultiplier = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.w; \n";
    }
  
    if (usesColors) {
      if (isTranslucent) {
        vs += "    vec4 color = a_color; \n";
      } else if (isRGB565) {
        vs +=
          "    float compressed = a_color; \n" +
          "    float r = floor(compressed * SHIFT_RIGHT_11); \n" +
          "    compressed -= r * SHIFT_LEFT_11; \n" +
          "    float g = floor(compressed * SHIFT_RIGHT_5); \n" +
          "    compressed -= g * SHIFT_LEFT_5; \n" +
          "    float b = compressed; \n" +
          "    vec3 rgb = vec3(r * NORMALIZE_5, g * NORMALIZE_6, b * NORMALIZE_5); \n" +
          "    vec4 color = vec4(rgb, 1.0); \n";
      } else {
        vs += "    vec4 color = vec4(a_color, 1.0); \n";
      }
    } else {
      vs += "    vec4 color = u_constantColor; \n";
    }
  
    if (isQuantized || isQuantizedDraco) {
      vs +=
        "    vec3 position = a_position * u_quantizedVolumeScaleAndOctEncodedRange.xyz; \n";
    } else {
      vs += "    vec3 position = a_position; \n";
    }
    vs +=
      "    vec3 position_absolute = vec3(czm_model * vec4(position, 1.0)); \n";
  
    if (usesNormals) {
      if (isOctEncoded16P) {
        vs += "    vec3 normal = czm_octDecode(a_normal); \n";
      } else if (isOctEncodedDraco) {
        // Draco oct-encoding decodes to zxy order
        vs +=
          "    vec3 normal = czm_octDecode(a_normal, u_quantizedVolumeScaleAndOctEncodedRange.w).zxy; \n";
      } else {
        vs += "    vec3 normal = a_normal; \n";
      }
      vs += "    vec3 normalEC = czm_normal * normal; \n";
    } else {
      vs += "    vec3 normal = vec3(1.0); \n";
    }
  
    if (hasColorStyle) {
      vs +=
        "    color = getColorFromStyle(position, position_absolute, color, normal); \n";
    }
  
    if (hasShowStyle) {
      vs +=
        "    float show = float(getShowFromStyle(position, position_absolute, color, normal)); \n";
    }
  
    if (hasPointSizeStyle) {
      vs +=
        "    gl_PointSize = getPointSizeFromStyle(position, position_absolute, color, normal) * czm_pixelRatio; \n";
    } else if (attenuation) {
      vs +=
        "    vec4 positionEC = czm_modelView * vec4(position, 1.0); \n" +
        "    float depth = -positionEC.z; \n" +
        // compute SSE for this point
        "    gl_PointSize = min((u_geometricError / depth) * u_depthMultiplier, u_pointSize); \n";
    } else {
      vs += "    gl_PointSize = u_pointSize; \n";
    }
  
    vs += "    color = color * u_highlightColor; \n";
  
    if (usesNormals && normalShading) {
      vs +=
        "    float diffuseStrength = czm_getLambertDiffuse(czm_lightDirectionEC, normalEC); \n" +
        "    diffuseStrength = max(diffuseStrength, 0.4); \n" + // Apply some ambient lighting
        "    color.xyz *= diffuseStrength * czm_lightColor; \n";
    }
  
    vs +=
      "    v_color = color; \n" +
      "    gl_Position = czm_modelViewProjection * vec4(position, 1.0); \n";
  
    if (usesNormals && backFaceCulling) {
      vs +=
        "    float visible = step(-normalEC.z, 0.0); \n" +
        "    gl_Position *= visible; \n" +
        "    gl_PointSize *= visible; \n";
    }
  
    if (hasShowStyle) {
      vs +=
        "    gl_Position.w *= float(show); \n" +
        "    gl_PointSize *= float(show); \n";
    }
  
    vs += "} \n";
  
    var fs = "varying vec4 v_color; \n";
  
    if (hasClippedContent) {
      fs +=
        "uniform highp sampler2D u_clippingPlanes; \n" +
        "uniform mat4 u_clippingPlanesMatrix; \n" +
        "uniform vec4 u_clippingPlanesEdgeStyle; \n";
      fs += "\n";
      fs += getClippingFunction(clippingPlanes, context);
      fs += "\n";
    }
  
    fs +=
      "void main() \n" +
      "{ \n" +
      "    gl_FragColor = czm_gammaCorrect(v_color); \n";
  
    if (hasClippedContent) {
      fs += getClipAndStyleCode(
        "u_clippingPlanes",
        "u_clippingPlanesMatrix",
        "u_clippingPlanesEdgeStyle"
      );
    }
  
    fs += "} \n";
  
    if (defined(pointCloud._vertexShaderLoaded)) {
      vs = pointCloud._vertexShaderLoaded(vs);
    }
  
    if (defined(pointCloud._fragmentShaderLoaded)) {
      fs = pointCloud._fragmentShaderLoaded(fs);
    }
  
    var drawCommand = pointCloud._drawCommand;
    if (defined(drawCommand.shaderProgram)) {
      // Destroy the old shader
      drawCommand.shaderProgram.destroy();
    }
    drawCommand.shaderProgram = ShaderProgram.fromCache({
      context: context,
      vertexShaderSource: vs,
      fragmentShaderSource: fs,
      attributeLocations: attributeLocations,
    });
  
    try {
      // Check if the shader compiles correctly. If not there is likely a syntax error with the style.
      drawCommand.shaderProgram._bind();
    } catch (error) {
      // Rephrase the error.
      throw new RuntimeError(
        "Error generating style shader: this may be caused by a type mismatch, index out-of-bounds, or other syntax error."
      );
    }
  }
  
  function decodeDraco(pointCloud, context) {
    if (pointCloud._decodingState === DecodingState.READY) {
      return false;
    }
    if (pointCloud._decodingState === DecodingState.NEEDS_DECODE) {
      var parsedContent = pointCloud._parsedContent;
      var draco = parsedContent.draco;
      var decodePromise = DracoLoader.decodePointCloud(draco, context);
      if (defined(decodePromise)) {
        pointCloud._decodingState = DecodingState.DECODING;
        decodePromise
          .then(function (result) {
            pointCloud._decodingState = DecodingState.READY;
            var decodedPositions = defined(result.POSITION)
              ? result.POSITION.array
              : undefined;
            var decodedRgb = defined(result.RGB) ? result.RGB.array : undefined;
            var decodedRgba = defined(result.RGBA)
              ? result.RGBA.array
              : undefined;
            var decodedNormals = defined(result.NORMAL)
              ? result.NORMAL.array
              : undefined;
            var decodedBatchIds = defined(result.BATCH_ID)
              ? result.BATCH_ID.array
              : undefined;
            var isQuantizedDraco =
              defined(decodedPositions) &&
              defined(result.POSITION.data.quantization);
            var isOctEncodedDraco =
              defined(decodedNormals) && defined(result.NORMAL.data.quantization);
            if (isQuantizedDraco) {
              // 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
              var quantization = result.POSITION.data.quantization;
              var range = quantization.range;
              pointCloud._quantizedVolumeScale = Cartesian3.fromElements(
                range,
                range,
                range
              );
              pointCloud._quantizedVolumeOffset = Cartesian3.unpack(
                quantization.minValues
              );
              pointCloud._quantizedRange =
                (1 << quantization.quantizationBits) - 1.0;
              pointCloud._isQuantizedDraco = true;
            }
            if (isOctEncodedDraco) {
              pointCloud._octEncodedRange =
                (1 << result.NORMAL.data.quantization.quantizationBits) - 1.0;
              pointCloud._isOctEncodedDraco = true;
            }
            var styleableProperties = parsedContent.styleableProperties;
            var batchTableProperties = draco.batchTableProperties;
            for (var name in batchTableProperties) {
              if (batchTableProperties.hasOwnProperty(name)) {
                var property = result[name];
                if (!defined(styleableProperties)) {
                  styleableProperties = {};
                }
                styleableProperties[name] = {
                  typedArray: property.array,
                  componentCount: property.data.componentsPerAttribute,
                };
              }
            }
            parsedContent.positions = defaultValue(
              decodedPositions,
              parsedContent.positions
            );
            parsedContent.colors = defaultValue(
              defaultValue(decodedRgba, decodedRgb),
              parsedContent.colors
            );
            parsedContent.normals = defaultValue(
              decodedNormals,
              parsedContent.normals
            );
            parsedContent.batchIds = defaultValue(
              decodedBatchIds,
              parsedContent.batchIds
            );
            parsedContent.styleableProperties = styleableProperties;
          })
          .otherwise(function (error) {
            pointCloud._decodingState = DecodingState.FAILED;
            pointCloud._readyPromise.reject(error);
          });
      }
    }
    return true;
  }
  
  var scratchComputedTranslation = new Cartesian4();
  var scratchScale = new Cartesian3();
  
  PointCloud.prototype.update = function (frameState) {
    var context = frameState.context;
    var decoding = decodeDraco(this, context);
    if (decoding) {
      return;
    }
  
    var shadersDirty = false;
    var modelMatrixDirty = !Matrix4.equals(this._modelMatrix, this.modelMatrix);
  
    if (this._mode !== frameState.mode) {
      this._mode = frameState.mode;
      modelMatrixDirty = true;
    }
  
    if (!defined(this._drawCommand)) {
      createResources(this, frameState);
      modelMatrixDirty = true;
      shadersDirty = true;
      this._ready = true;
      this._readyPromise.resolve(this);
      this._parsedContent = undefined; // Unload
    }
  
    if (modelMatrixDirty) {
      Matrix4.clone(this.modelMatrix, this._modelMatrix);
      var modelMatrix = this._drawCommand.modelMatrix;
      Matrix4.clone(this._modelMatrix, modelMatrix);
  
      if (defined(this._rtcCenter)) {
        Matrix4.multiplyByTranslation(modelMatrix, this._rtcCenter, modelMatrix);
      }
      if (defined(this._quantizedVolumeOffset)) {
        Matrix4.multiplyByTranslation(
          modelMatrix,
          this._quantizedVolumeOffset,
          modelMatrix
        );
      }
  
      if (frameState.mode !== SceneMode.SCENE3D) {
        var projection = frameState.mapProjection;
        var translation = Matrix4.getColumn(
          modelMatrix,
          3,
          scratchComputedTranslation
        );
        if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {
          Transforms.basisTo2D(projection, modelMatrix, modelMatrix);
        }
      }
  
      var boundingSphere = this._drawCommand.boundingVolume;
      BoundingSphere.clone(this._boundingSphere, boundingSphere);
  
      if (this._cull) {
        var center = boundingSphere.center;
        Matrix4.multiplyByPoint(modelMatrix, center, center);
        var scale = Matrix4.getScale(modelMatrix, scratchScale);
        boundingSphere.radius *= Cartesian3.maximumComponent(scale);
      }
    }
  
    if (this.clippingPlanesDirty) {
      this.clippingPlanesDirty = false;
      shadersDirty = true;
    }
  
    if (this._attenuation !== this.attenuation) {
      this._attenuation = this.attenuation;
      shadersDirty = true;
    }
  
    if (this.backFaceCulling !== this._backFaceCulling) {
      this._backFaceCulling = this.backFaceCulling;
      shadersDirty = true;
    }
  
    if (this.normalShading !== this._normalShading) {
      this._normalShading = this.normalShading;
      shadersDirty = true;
    }
  
    if (this._style !== this.style || this.styleDirty) {
      this._style = this.style;
      this.styleDirty = false;
      shadersDirty = true;
    }
  
    if (shadersDirty) {
      createShaders(this, frameState, this._style);
    }
  
    this._drawCommand.castShadows = ShadowMode.castShadows(this.shadows);
    this._drawCommand.receiveShadows = ShadowMode.receiveShadows(this.shadows);
  
    // Update the render state
    var isTranslucent =
      this._highlightColor.alpha < 1.0 ||
      this._constantColor.alpha < 1.0 ||
      this._styleTranslucent;
    this._drawCommand.renderState = isTranslucent
      ? this._translucentRenderState
      : this._opaqueRenderState;
    this._drawCommand.pass = isTranslucent ? Pass.TRANSLUCENT : this._opaquePass;
  
    var commandList = frameState.commandList;
  
    var passes = frameState.passes;
    if (passes.render || passes.pick) {
      commandList.push(this._drawCommand);
    }
  };
  
  PointCloud.prototype.isDestroyed = function () {
    return false;
  };
  
  PointCloud.prototype.destroy = function () {
    var command = this._drawCommand;
    if (defined(command)) {
      command.vertexArray = command.vertexArray && command.vertexArray.destroy();
      command.shaderProgram =
        command.shaderProgram && command.shaderProgram.destroy();
    }
    return destroyObject(this);
  };
  export default PointCloud;
  