static readAttributeAsRawCompactTypedArray(attribute) {
    //>>includeStart('debug', pragmas.debug);
    Check.defined("attribute", attribute);
    //>>includeEnd('debug');

    const elementType = attribute.type;
    const elementCount = attribute.count;

    const componentsPerElement =
      AttributeType.getNumberOfComponents(elementType);
    const totalComponentCount = elementCount * componentsPerElement;

    // If the data is quantized, use the quantized component type
    let componentType = attribute.componentDatatype;
    const quantization = attribute.quantization;
    if (defined(quantization)) {
      componentType = quantization.componentDatatype;
    }
    const buffer = attribute.buffer;

    // If the byte stride is the default (i.e. the total element size),
    // then just fetch the whole buffer data into a typed array of the
    // desired target type, and return it
    const byteOffset = attribute.byteOffset;
    const byteStride = attribute.byteStride;
    const bytesPerComponent = ComponentDatatype.getSizeInBytes(componentType);
    const defaultByteStride = componentsPerElement * bytesPerComponent;
    if (!defined(byteStride) || byteStride === defaultByteStride) {
      const typedArray = ComponentDatatype.createTypedArray(
        componentType,
        totalComponentCount,
      );
      buffer.getBufferData(typedArray, byteOffset);
      return typedArray;
    }

    // Fetch the whole buffer in its raw form, to pick out the
    // interleaved values.
    // Note: When ALL attributes have to be fetched from an
    // interleaved buffer, then this getBufferData call will
    // be performed multiple times. It would be preferable to
    // have ONE "TypedArray[] getThemFrom(buffer)" call that
    // returns all of the (interleaved) attributes at once,
    // but this requires abstractions that we don't have.
    const fullTypedArray = new Uint8Array(buffer.sizeInBytes);
    buffer.getBufferData(fullTypedArray);

    // Read the components of each element, and write them into
    // a typed array in a compact form
    const compactTypedArray = ComponentDatatype.createTypedArray(
      componentType,
      totalComponentCount,
    );
    const elementByteStride = byteStride ?? defaultByteStride;
    const dataView = new DataView(
      fullTypedArray.buffer,
      fullTypedArray.byteOffset,
      fullTypedArray.byteLength,
    );
    const components = new Array(componentsPerElement);
    const componentsReader = ModelReader.createComponentsReader(componentType);
    for (let i = 0; i < elementCount; ++i) {
      const elementByteOffset = byteOffset + i * elementByteStride;
      componentsReader(
        dataView,
        elementByteOffset,
        componentsPerElement,
        components,
      );
      for (let j = 0; j < componentsPerElement; ++j) {
        compactTypedArray[i * componentsPerElement + j] = components[j];
      }
    }
    return compactTypedArray;
  }
Read the data of the given attribute into a compact typed array.

If the attribute is stored as interleaved data, then the result
will be the deinterleaved data. If the data is quantized or
normalized, then the resulting data will be the "raw" data,
without applying normalization or dequantization.

@param {ModelComponents.Attribute} attribute The attribute
@returns {TypedArray} The raw attribute data