text: import AttributeCompression from '../Core/AttributeCompression.js';
  import Cartesian3 from '../Core/Cartesian3.js';
  import Color from '../Core/Color.js';
  import ComponentDatatype from '../Core/ComponentDatatype.js';
  import defaultValue from '../Core/defaultValue.js';
  import defined from '../Core/defined.js';
  import deprecationWarning from '../Core/deprecationWarning.js';
  import destroyObject from '../Core/destroyObject.js';
  import DeveloperError from '../Core/DeveloperError.js';
  import Ellipsoid from '../Core/Ellipsoid.js';
  import getStringFromTypedArray from '../Core/getStringFromTypedArray.js';
  import Matrix3 from '../Core/Matrix3.js';
  import Matrix4 from '../Core/Matrix4.js';
  import Quaternion from '../Core/Quaternion.js';
  import RequestType from '../Core/RequestType.js';
  import RuntimeError from '../Core/RuntimeError.js';
  import Transforms from '../Core/Transforms.js';
  import TranslationRotationScale from '../Core/TranslationRotationScale.js';
  import Pass from '../Renderer/Pass.js';
  import Axis from './Axis.js';
  import Cesium3DTileBatchTable from './Cesium3DTileBatchTable.js';
  import Cesium3DTileFeature from './Cesium3DTileFeature.js';
  import Cesium3DTileFeatureTable from './Cesium3DTileFeatureTable.js';
  import ModelInstanceCollection from './ModelInstanceCollection.js';
  
      /**
       * Represents the contents of a
       * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Instanced3DModel|Instanced 3D Model}
       * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset.
       * <p>
       * Implements the {@link Cesium3DTileContent} interface.
       * </p>
       *
       * @alias Instanced3DModel3DTileContent
       * @constructor
       *
       * @private
       */
      function Instanced3DModel3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) {
          this._tileset = tileset;
          this._tile = tile;
          this._resource = resource;
          this._modelInstanceCollection = undefined;
          this._batchTable = undefined;
          this._features = undefined;
  
          this.featurePropertiesDirty = false;
  
          initialize(this, arrayBuffer, byteOffset);
      }
  
      // This can be overridden for testing purposes
      Instanced3DModel3DTileContent._deprecationWarning = deprecationWarning;
  
      Object.defineProperties(Instanced3DModel3DTileContent.prototype, {
          featuresLength : {
              get : function() {
                  return this._batchTable.featuresLength;
              }
          },
  
          pointsLength : {
              get : function() {
                  return 0;
              }
          },
  
          trianglesLength : {
              get : function() {
                  var model = this._modelInstanceCollection._model;
                  if (defined(model)) {
                      return model.trianglesLength;
                  }
                  return 0;
              }
          },
  
          geometryByteLength : {
              get : function() {
                  var model = this._modelInstanceCollection._model;
                  if (defined(model)) {
                      return model.geometryByteLength;
                  }
                  return 0;
              }
          },
  
          texturesByteLength : {
              get : function() {
                  var model = this._modelInstanceCollection._model;
                  if (defined(model)) {
                      return model.texturesByteLength;
                  }
                  return 0;
              }
          },
  
          batchTableByteLength : {
              get : function() {
                  return this._batchTable.memorySizeInBytes;
              }
          },
  
          innerContents : {
              get : function() {
                  return undefined;
              }
          },
  
          readyPromise : {
              get : function() {
                  return this._modelInstanceCollection.readyPromise;
              }
          },
  
          tileset : {
              get : function() {
                  return this._tileset;
              }
          },
  
          tile : {
              get : function() {
                  return this._tile;
              }
          },
  
          url: {
              get: function() {
                  return this._resource.getUrlComponent(true);
              }
          },
  
          batchTable : {
              get : function() {
                  return this._batchTable;
              }
          }
      });
  
      function getPickIdCallback(content) {
          return function() {
              return content._batchTable.getPickId();
          };
      }
  
      var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
      var propertyScratch1 = new Array(4);
      var propertyScratch2 = new Array(4);
  
      function initialize(content, arrayBuffer, byteOffset) {
          var byteStart = defaultValue(byteOffset, 0);
          byteOffset = byteStart;
  
          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 Instanced 3D Model version 1 is supported. Version ' + version + ' is not.');
          }
          byteOffset += sizeOfUint32;
  
          var byteLength = view.getUint32(byteOffset, true);
          byteOffset += sizeOfUint32;
  
          var featureTableJsonByteLength = view.getUint32(byteOffset, true);
          if (featureTableJsonByteLength === 0) {
              throw new RuntimeError('featureTableJsonByteLength is zero, the feature table must be defined.');
          }
          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 gltfFormat = view.getUint32(byteOffset, true);
          if (gltfFormat !== 1 && gltfFormat !== 0) {
              throw new RuntimeError('Only glTF format 0 (uri) or 1 (embedded) are supported. Format ' + gltfFormat + ' is not.');
          }
          byteOffset += sizeOfUint32;
  
          var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength);
          var featureTableJson = JSON.parse(featureTableString);
          byteOffset += featureTableJsonByteLength;
  
          var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength);
          byteOffset += featureTableBinaryByteLength;
  
          var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary);
          var instancesLength = featureTable.getGlobalProperty('INSTANCES_LENGTH');
          featureTable.featuresLength = instancesLength;
  
          if (!defined(instancesLength)) {
              throw new RuntimeError('Feature table global property: INSTANCES_LENGTH must be defined');
          }
  
          var batchTableJson;
          var batchTableBinary;
          if (batchTableJsonByteLength > 0) {
              var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJsonByteLength);
              batchTableJson = JSON.parse(batchTableString);
              byteOffset += batchTableJsonByteLength;
  
              if (batchTableBinaryByteLength > 0) {
                  // Has a batch table binary
                  batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength);
                  // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
                  batchTableBinary = new Uint8Array(batchTableBinary);
                  byteOffset += batchTableBinaryByteLength;
              }
          }
  
          content._batchTable = new Cesium3DTileBatchTable(content, instancesLength, batchTableJson, batchTableBinary);
  
          var gltfByteLength = byteStart + byteLength - byteOffset;
          if (gltfByteLength === 0) {
              throw new RuntimeError('glTF byte length is zero, i3dm must have a glTF to instance.');
          }
  
          var gltfView;
          if (byteOffset % 4 === 0) {
              gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);
          } else {
              // Create a copy of the glb so that it is 4-byte aligned
              Instanced3DModel3DTileContent._deprecationWarning('i3dm-glb-unaligned', 'The embedded glb is not aligned to a 4-byte boundary.');
              gltfView = new Uint8Array(uint8Array.subarray(byteOffset, byteOffset + gltfByteLength));
          }
  
          var tileset = content._tileset;
  
          // Create model instance collection
          var collectionOptions = {
              instances : new Array(instancesLength),
              batchTable : content._batchTable,
              cull : false, // Already culled by 3D Tiles
              url : undefined,
              requestType : RequestType.TILES3D,
              gltf : undefined,
              basePath : undefined,
              incrementallyLoadTextures : false,
              upAxis : tileset._gltfUpAxis,
              forwardAxis : Axis.X,
              opaquePass : Pass.CESIUM_3D_TILE, // Draw opaque portions during the 3D Tiles pass
              pickIdLoaded : getPickIdCallback(content),
              imageBasedLightingFactor : tileset.imageBasedLightingFactor,
              lightColor : tileset.lightColor,
              luminanceAtZenith : tileset.luminanceAtZenith,
              sphericalHarmonicCoefficients : tileset.sphericalHarmonicCoefficients,
              specularEnvironmentMaps : tileset.specularEnvironmentMaps
          };
  
          if (gltfFormat === 0) {
              var gltfUrl = getStringFromTypedArray(gltfView);
  
              // We need to remove padding from the end of the model URL in case this tile was part of a composite tile.
              // This removes all white space and null characters from the end of the string.
              gltfUrl = gltfUrl.replace(/[\s\0]+$/, '');
              collectionOptions.url = content._resource.getDerivedResource({
                  url: gltfUrl
              });
          } else {
              collectionOptions.gltf = gltfView;
              collectionOptions.basePath = content._resource.clone();
          }
  
          var eastNorthUp = featureTable.getGlobalProperty('EAST_NORTH_UP');
  
          var rtcCenter;
          var rtcCenterArray = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3);
          if (defined(rtcCenterArray)) {
              rtcCenter = Cartesian3.unpack(rtcCenterArray);
          }
  
          var instances = collectionOptions.instances;
          var instancePosition = new Cartesian3();
          var instancePositionArray = new Array(3);
          var instanceNormalRight = new Cartesian3();
          var instanceNormalUp = new Cartesian3();
          var instanceNormalForward = new Cartesian3();
          var instanceRotation = new Matrix3();
          var instanceQuaternion = new Quaternion();
          var instanceScale = new Cartesian3();
          var instanceTranslationRotationScale = new TranslationRotationScale();
          var instanceTransform = new Matrix4();
          for (var i = 0; i < instancesLength; i++) {
              // Get the instance position
              var position = featureTable.getProperty('POSITION', ComponentDatatype.FLOAT, 3, i, propertyScratch1);
              if (!defined(position)) {
                  position = instancePositionArray;
                  var positionQuantized = featureTable.getProperty('POSITION_QUANTIZED', ComponentDatatype.UNSIGNED_SHORT, 3, i, propertyScratch1);
                  if (!defined(positionQuantized)) {
                      throw new RuntimeError('Either POSITION or POSITION_QUANTIZED must be defined for each instance.');
                  }
                  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.');
                  }
                  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.');
                  }
                  for (var j = 0; j < 3; j++) {
                      position[j] = (positionQuantized[j] / 65535.0 * quantizedVolumeScale[j]) + quantizedVolumeOffset[j];
                  }
              }
              Cartesian3.unpack(position, 0, instancePosition);
              if (defined(rtcCenter)) {
                  Cartesian3.add(instancePosition, rtcCenter, instancePosition);
              }
              instanceTranslationRotationScale.translation = instancePosition;
  
              // Get the instance rotation
              var normalUp = featureTable.getProperty('NORMAL_UP', ComponentDatatype.FLOAT, 3, i, propertyScratch1);
              var normalRight = featureTable.getProperty('NORMAL_RIGHT', ComponentDatatype.FLOAT, 3, i, propertyScratch2);
              var hasCustomOrientation = false;
              if (defined(normalUp)) {
                  if (!defined(normalRight)) {
                      throw new RuntimeError('To define a custom orientation, both NORMAL_UP and NORMAL_RIGHT must be defined.');
                  }
                  Cartesian3.unpack(normalUp, 0, instanceNormalUp);
                  Cartesian3.unpack(normalRight, 0, instanceNormalRight);
                  hasCustomOrientation = true;
              } else {
                  var octNormalUp = featureTable.getProperty('NORMAL_UP_OCT32P', ComponentDatatype.UNSIGNED_SHORT, 2, i, propertyScratch1);
                  var octNormalRight = featureTable.getProperty('NORMAL_RIGHT_OCT32P', ComponentDatatype.UNSIGNED_SHORT, 2, i, propertyScratch2);
                  if (defined(octNormalUp)) {
                      if (!defined(octNormalRight)) {
                          throw new RuntimeError('To define a custom orientation with oct-encoded vectors, both NORMAL_UP_OCT32P and NORMAL_RIGHT_OCT32P must be defined.');
                      }
                      AttributeCompression.octDecodeInRange(octNormalUp[0], octNormalUp[1], 65535, instanceNormalUp);
                      AttributeCompression.octDecodeInRange(octNormalRight[0], octNormalRight[1], 65535, instanceNormalRight);
                      hasCustomOrientation = true;
                  } else if (eastNorthUp) {
                      Transforms.eastNorthUpToFixedFrame(instancePosition, Ellipsoid.WGS84, instanceTransform);
                      Matrix4.getMatrix3(instanceTransform, instanceRotation);
                  } else {
                      Matrix3.clone(Matrix3.IDENTITY, instanceRotation);
                  }
              }
              if (hasCustomOrientation) {
                  Cartesian3.cross(instanceNormalRight, instanceNormalUp, instanceNormalForward);
                  Cartesian3.normalize(instanceNormalForward, instanceNormalForward);
                  Matrix3.setColumn(instanceRotation, 0, instanceNormalRight, instanceRotation);
                  Matrix3.setColumn(instanceRotation, 1, instanceNormalUp, instanceRotation);
                  Matrix3.setColumn(instanceRotation, 2, instanceNormalForward, instanceRotation);
              }
              Quaternion.fromRotationMatrix(instanceRotation, instanceQuaternion);
              instanceTranslationRotationScale.rotation = instanceQuaternion;
  
              // Get the instance scale
              instanceScale = Cartesian3.fromElements(1.0, 1.0, 1.0, instanceScale);
              var scale = featureTable.getProperty('SCALE', ComponentDatatype.FLOAT, 1, i);
              if (defined(scale)) {
                  Cartesian3.multiplyByScalar(instanceScale, scale, instanceScale);
              }
              var nonUniformScale = featureTable.getProperty('SCALE_NON_UNIFORM', ComponentDatatype.FLOAT, 3, i, propertyScratch1);
              if (defined(nonUniformScale)) {
                  instanceScale.x *= nonUniformScale[0];
                  instanceScale.y *= nonUniformScale[1];
                  instanceScale.z *= nonUniformScale[2];
              }
              instanceTranslationRotationScale.scale = instanceScale;
  
              // Get the batchId
              var batchId = featureTable.getProperty('BATCH_ID', ComponentDatatype.UNSIGNED_SHORT, 1, i);
              if (!defined(batchId)) {
                  // If BATCH_ID semantic is undefined, batchId is just the instance number
                  batchId = i;
              }
  
              // Create the model matrix and the instance
              Matrix4.fromTranslationRotationScale(instanceTranslationRotationScale, instanceTransform);
              var modelMatrix = instanceTransform.clone();
              instances[i] = {
                  modelMatrix : modelMatrix,
                  batchId : batchId
              };
          }
  
          content._modelInstanceCollection = new ModelInstanceCollection(collectionOptions);
      }
  
      function createFeatures(content) {
          var featuresLength = content.featuresLength;
          if (!defined(content._features) && (featuresLength > 0)) {
              var features = new Array(featuresLength);
              for (var i = 0; i < featuresLength; ++i) {
                  features[i] = new Cesium3DTileFeature(content, i);
              }
              content._features = features;
          }
      }
  
      Instanced3DModel3DTileContent.prototype.hasProperty = function(batchId, name) {
          return this._batchTable.hasProperty(batchId, name);
      };
  
      Instanced3DModel3DTileContent.prototype.getFeature = function(batchId) {
          var featuresLength = this.featuresLength;
          //>>includeStart('debug', pragmas.debug);
          if (!defined(batchId) || (batchId < 0) || (batchId >= featuresLength)) {
              throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + (featuresLength - 1) + ').');
          }
          //>>includeEnd('debug');
  
          createFeatures(this);
          return this._features[batchId];
      };
  
      Instanced3DModel3DTileContent.prototype.applyDebugSettings = function(enabled, color) {
          color = enabled ? color : Color.WHITE;
          this._batchTable.setAllColor(color);
      };
  
      Instanced3DModel3DTileContent.prototype.applyStyle = function(style) {
          this._batchTable.applyStyle(style);
      };
  
      Instanced3DModel3DTileContent.prototype.update = function(tileset, frameState) {
          var commandStart = frameState.commandList.length;
  
          // In the PROCESSING state we may be calling update() to move forward
          // the content's resource loading.  In the READY state, it will
          // actually generate commands.
          this._batchTable.update(tileset, frameState);
          this._modelInstanceCollection.modelMatrix = this._tile.computedTransform;
          this._modelInstanceCollection.shadows = this._tileset.shadows;
          this._modelInstanceCollection.lightColor = this._tileset.lightColor;
          this._modelInstanceCollection.luminanceAtZenith = this._tileset.luminanceAtZenith;
          this._modelInstanceCollection.sphericalHarmonicCoefficients = this._tileset.sphericalHarmonicCoefficients;
          this._modelInstanceCollection.specularEnvironmentMaps = this._tileset.specularEnvironmentMaps;
          this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe;
  
          var model = this._modelInstanceCollection._model;
  
          if (defined(model)) {
              // Update for clipping planes
              var tilesetClippingPlanes = this._tileset.clippingPlanes;
              model.clippingPlanesOriginMatrix = this._tileset.clippingPlanesOriginMatrix;
              if (defined(tilesetClippingPlanes) && this._tile.clippingPlanesDirty) {
                  // Dereference the clipping planes from the model if they are irrelevant - saves on shading
                  // Link/Dereference directly to avoid ownership checks.
                  model._clippingPlanes = (tilesetClippingPlanes.enabled && this._tile._isClipped) ? tilesetClippingPlanes : undefined;
              }
  
              // If the model references a different ClippingPlaneCollection due to the tileset's collection being replaced with a
              // ClippingPlaneCollection that gives this tile the same clipping status, update the model to use the new ClippingPlaneCollection.
              if (defined(tilesetClippingPlanes) && defined(model._clippingPlanes) && model._clippingPlanes !== tilesetClippingPlanes) {
                  model._clippingPlanes = tilesetClippingPlanes;
              }
          }
  
          this._modelInstanceCollection.update(frameState);
  
          // If any commands were pushed, add derived commands
          var commandEnd = frameState.commandList.length;
          if ((commandStart < commandEnd) && (frameState.passes.render || frameState.passes.pick)) {
              this._batchTable.addDerivedCommands(frameState, commandStart, false);
          }
      };
  
      Instanced3DModel3DTileContent.prototype.isDestroyed = function() {
          return false;
      };
  
      Instanced3DModel3DTileContent.prototype.destroy = function() {
          this._modelInstanceCollection = this._modelInstanceCollection && this._modelInstanceCollection.destroy();
          this._batchTable = this._batchTable && this._batchTable.destroy();
  
          return destroyObject(this);
      };
  export default Instanced3DModel3DTileContent;
  