| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 | import arrayFill from "../Core/arrayFill.js";import Check from "../Core/Check.js";import defaultValue from "../Core/defaultValue.js";import defer from "../Core/defer.js";import defined from "../Core/defined.js";import DeveloperError from "../Core/DeveloperError.js";import Buffer from "../Renderer/Buffer.js";import BufferUsage from "../Renderer/BufferUsage.js";import AttributeType from "./AttributeType.js";import JobType from "./JobType.js";import ModelComponents from "./ModelComponents.js";import ResourceLoader from "./ResourceLoader.js";import ResourceLoaderState from "./ResourceLoaderState.js";import AttributeCompression from "../Core/AttributeCompression.js";import ComponentDatatype from "../Core/ComponentDatatype.js";/** * Loads a vertex buffer from a glTF buffer view. * <p> * Implements the {@link ResourceLoader} interface. * </p> * * @alias GltfVertexBufferLoader * @constructor * @augments ResourceLoader * * @param {Object} options Object with the following properties: * @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies). * @param {Object} options.gltf The glTF JSON. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. * @param {Number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer. * @param {Object} [options.draco] The Draco extension object. * @param {String} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL. * @param {Number} [options.accessorId] The accessor id. * @param {String} [options.cacheKey] The cache key of the resource. * @param {Boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created. * @param {Boolean} [options.dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU. * @param {Boolean} [options.loadAsTypedArray=false] Load vertex buffer as a typed array instead of a GPU vertex buffer. * * @exception {DeveloperError} One of options.bufferViewId and options.draco must be defined. * @exception {DeveloperError} When options.draco is defined options.attributeSemantic must also be defined. * @exception {DeveloperError} When options.draco is defined options.accessorId must also be defined. * * @private */export default function GltfVertexBufferLoader(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const resourceCache = options.resourceCache;  const gltf = options.gltf;  const gltfResource = options.gltfResource;  const baseResource = options.baseResource;  const bufferViewId = options.bufferViewId;  const draco = options.draco;  const attributeSemantic = options.attributeSemantic;  const accessorId = options.accessorId;  const cacheKey = options.cacheKey;  const asynchronous = defaultValue(options.asynchronous, true);  const dequantize = defaultValue(options.dequantize, false);  const loadAsTypedArray = defaultValue(options.loadAsTypedArray, false);  //>>includeStart('debug', pragmas.debug);  Check.typeOf.func("options.resourceCache", resourceCache);  Check.typeOf.object("options.gltf", gltf);  Check.typeOf.object("options.gltfResource", gltfResource);  Check.typeOf.object("options.baseResource", baseResource);  const hasBufferViewId = defined(bufferViewId);  const hasDraco = defined(draco);  const hasAttributeSemantic = defined(attributeSemantic);  const hasAccessorId = defined(accessorId);  if (hasBufferViewId === hasDraco) {    throw new DeveloperError(      "One of options.bufferViewId and options.draco must be defined."    );  }  if (hasDraco && !hasAttributeSemantic) {    throw new DeveloperError(      "When options.draco is defined options.attributeSemantic must also be defined."    );  }  if (hasDraco && !hasAccessorId) {    throw new DeveloperError(      "When options.draco is defined options.accessorId must also be defined."    );  }  if (hasDraco) {    Check.typeOf.object("options.draco", draco);    Check.typeOf.string("options.attributeSemantic", attributeSemantic);    Check.typeOf.number("options.accessorId", accessorId);  }  //>>includeEnd('debug');  this._resourceCache = resourceCache;  this._gltfResource = gltfResource;  this._baseResource = baseResource;  this._gltf = gltf;  this._bufferViewId = bufferViewId;  this._draco = draco;  this._attributeSemantic = attributeSemantic;  this._accessorId = accessorId;  this._cacheKey = cacheKey;  this._asynchronous = asynchronous;  this._dequantize = dequantize;  this._loadAsTypedArray = loadAsTypedArray;  this._bufferViewLoader = undefined;  this._dracoLoader = undefined;  this._quantization = undefined;  this._typedArray = undefined;  this._buffer = undefined;  this._state = ResourceLoaderState.UNLOADED;  this._promise = defer();}if (defined(Object.create)) {  GltfVertexBufferLoader.prototype = Object.create(ResourceLoader.prototype);  GltfVertexBufferLoader.prototype.constructor = GltfVertexBufferLoader;}Object.defineProperties(GltfVertexBufferLoader.prototype, {  /**   * A promise that resolves to the resource when the resource is ready.   *   * @memberof GltfVertexBufferLoader.prototype   *   * @type {Promise.<GltfVertexBufferLoader>}   * @readonly   * @private   */  promise: {    get: function () {      return this._promise.promise;    },  },  /**   * The cache key of the resource.   *   * @memberof GltfVertexBufferLoader.prototype   *   * @type {String}   * @readonly   * @private   */  cacheKey: {    get: function () {      return this._cacheKey;    },  },  /**   * The vertex buffer. This is only defined when <code>loadAsTypedArray</code> is false.   *   * @memberof GltfVertexBufferLoader.prototype   *   * @type {Buffer}   * @readonly   * @private   */  buffer: {    get: function () {      return this._buffer;    },  },  /**   * The typed array containing vertex buffer data. This is only defined when <code>loadAsTypedArray</code> is true.   *   * @memberof GltfVertexBufferLoader.prototype   *   * @type {Uint8Array}   * @readonly   * @private   */  typedArray: {    get: function () {      return this._typedArray;    },  },  /**   * Information about the quantized vertex attribute after Draco decode.   *   * @memberof GltfVertexBufferLoader.prototype   *   * @type {ModelComponents.Quantization}   * @readonly   * @private   */  quantization: {    get: function () {      return this._quantization;    },  },});/** * Loads the resource. * @private */GltfVertexBufferLoader.prototype.load = function () {  if (defined(this._draco)) {    loadFromDraco(this);  } else {    loadFromBufferView(this);  }};function getQuantizationInformation(  dracoQuantization,  componentDatatype,  componentCount,  type) {  const quantizationBits = dracoQuantization.quantizationBits;  const normalizationRange = (1 << quantizationBits) - 1;  const normalizationDivisor = 1.0 / normalizationRange;  const quantization = new ModelComponents.Quantization();  quantization.componentDatatype = componentDatatype;  quantization.octEncoded = dracoQuantization.octEncoded;  quantization.octEncodedZXY = true;  quantization.type = type;  if (quantization.octEncoded) {    quantization.type = AttributeType.VEC2;    quantization.normalizationRange = normalizationRange;  } else {    const MathType = AttributeType.getMathType(type);    if (MathType === Number) {      const dimensions = dracoQuantization.range;      quantization.quantizedVolumeOffset = dracoQuantization.minValues[0];      quantization.quantizedVolumeDimensions = dimensions;      quantization.normalizationRange = normalizationRange;      quantization.quantizedVolumeStepSize = dimensions * normalizationDivisor;    } else {      quantization.quantizedVolumeOffset = MathType.unpack(        dracoQuantization.minValues      );      quantization.normalizationRange = MathType.unpack(        arrayFill(new Array(componentCount), normalizationRange)      );      const packedDimensions = arrayFill(        new Array(componentCount),        dracoQuantization.range      );      quantization.quantizedVolumeDimensions = MathType.unpack(        packedDimensions      );      // Computing the step size      const packedSteps = packedDimensions.map(function (dimension) {        return dimension * normalizationDivisor;      });      quantization.quantizedVolumeStepSize = MathType.unpack(packedSteps);    }  }  return quantization;}function loadFromDraco(vertexBufferLoader) {  const resourceCache = vertexBufferLoader._resourceCache;  const dracoLoader = resourceCache.loadDraco({    gltf: vertexBufferLoader._gltf,    draco: vertexBufferLoader._draco,    gltfResource: vertexBufferLoader._gltfResource,    baseResource: vertexBufferLoader._baseResource,  });  vertexBufferLoader._dracoLoader = dracoLoader;  vertexBufferLoader._state = ResourceLoaderState.LOADING;  dracoLoader.promise    .then(function () {      if (vertexBufferLoader.isDestroyed()) {        return;      }      // Get the typed array and quantization information      const decodedVertexAttributes = dracoLoader.decodedData.vertexAttributes;      const attributeSemantic = vertexBufferLoader._attributeSemantic;      const dracoAttribute = decodedVertexAttributes[attributeSemantic];      const accessorId = vertexBufferLoader._accessorId;      const accessor = vertexBufferLoader._gltf.accessors[accessorId];      const type = accessor.type;      const typedArray = dracoAttribute.array;      const dracoQuantization = dracoAttribute.data.quantization;      if (defined(dracoQuantization)) {        vertexBufferLoader._quantization = getQuantizationInformation(          dracoQuantization,          dracoAttribute.data.componentDatatype,          dracoAttribute.data.componentsPerAttribute,          type        );      }      // Now wait for process() to run to finish loading      vertexBufferLoader._typedArray = typedArray;      vertexBufferLoader._state = ResourceLoaderState.PROCESSING;    })    .catch(function (error) {      if (vertexBufferLoader.isDestroyed()) {        return;      }      handleError(vertexBufferLoader, error);    });}function loadFromBufferView(vertexBufferLoader) {  const resourceCache = vertexBufferLoader._resourceCache;  const bufferViewLoader = resourceCache.loadBufferView({    gltf: vertexBufferLoader._gltf,    bufferViewId: vertexBufferLoader._bufferViewId,    gltfResource: vertexBufferLoader._gltfResource,    baseResource: vertexBufferLoader._baseResource,  });  vertexBufferLoader._state = ResourceLoaderState.LOADING;  vertexBufferLoader._bufferViewLoader = bufferViewLoader;  bufferViewLoader.promise    .then(function () {      if (vertexBufferLoader.isDestroyed()) {        return;      }      // Now wait for process() to run to finish loading      vertexBufferLoader._typedArray = bufferViewLoader.typedArray;      vertexBufferLoader._state = ResourceLoaderState.PROCESSING;    })    .catch(function (error) {      if (vertexBufferLoader.isDestroyed()) {        return;      }      handleError(vertexBufferLoader, error);    });}function handleError(vertexBufferLoader, error) {  vertexBufferLoader.unload();  vertexBufferLoader._state = ResourceLoaderState.FAILED;  const errorMessage = "Failed to load vertex buffer";  error = vertexBufferLoader.getError(errorMessage, error);  vertexBufferLoader._promise.reject(error);}function CreateVertexBufferJob() {  this.typedArray = undefined;  this.dequantize = undefined;  this.componentType = undefined;  this.type = undefined;  this.count = undefined;  this.context = undefined;  this.buffer = undefined;}CreateVertexBufferJob.prototype.set = function (  typedArray,  dequantize,  componentType,  type,  count,  context) {  this.typedArray = typedArray;  this.dequantize = dequantize;  this.componentType = componentType;  this.type = type;  this.count = count;  this.context = context;};CreateVertexBufferJob.prototype.execute = function () {  this.buffer = createVertexBuffer(    this.typedArray,    this.dequantize,    this.componentType,    this.type,    this.count,    this.context  );};function createVertexBuffer(  typedArray,  dequantize,  componentType,  type,  count,  context) {  if (dequantize && componentType !== ComponentDatatype.FLOAT) {    typedArray = AttributeCompression.dequantize(      typedArray,      componentType,      type,      count    );  }  const buffer = Buffer.createVertexBuffer({    typedArray: typedArray,    context: context,    usage: BufferUsage.STATIC_DRAW,  });  buffer.vertexArrayDestroyable = false;  return buffer;}const scratchVertexBufferJob = new CreateVertexBufferJob();/** * Processes the resource until it becomes ready. * * @param {FrameState} frameState The frame state. * @private */GltfVertexBufferLoader.prototype.process = function (frameState) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("frameState", frameState);  //>>includeEnd('debug');  if (this._state === ResourceLoaderState.READY) {    return;  }  const typedArray = this._typedArray;  const dequantize = this._dequantize;  if (defined(this._dracoLoader)) {    this._dracoLoader.process(frameState);  }  if (defined(this._bufferViewLoader)) {    this._bufferViewLoader.process(frameState);  }  if (!defined(typedArray)) {    // Buffer view hasn't been loaded yet    return;  }  if (this._loadAsTypedArray) {    // Unload everything except the typed array    this.unload();    this._typedArray = typedArray;    this._state = ResourceLoaderState.READY;    this._promise.resolve(this);    return;  }  const accessor = this._gltf.accessors[this._accessorId];  let buffer;  if (this._asynchronous) {    const vertexBufferJob = scratchVertexBufferJob;    vertexBufferJob.set(      typedArray,      dequantize,      accessor.componentType,      accessor.type,      accessor.count,      frameState.context    );    const jobScheduler = frameState.jobScheduler;    if (!jobScheduler.execute(vertexBufferJob, JobType.BUFFER)) {      // Job scheduler is full. Try again next frame.      return;    }    buffer = vertexBufferJob.buffer;  } else {    buffer = createVertexBuffer(      typedArray,      dequantize,      accessor.componentType,      accessor.type,      accessor.count,      frameState.context    );  }  // Unload everything except the vertex buffer  this.unload();  this._buffer = buffer;  this._state = ResourceLoaderState.READY;  this._promise.resolve(this);};/** * Unloads the resource. * @private */GltfVertexBufferLoader.prototype.unload = function () {  if (defined(this._buffer)) {    this._buffer.destroy();  }  const resourceCache = this._resourceCache;  if (defined(this._bufferViewLoader)) {    resourceCache.unload(this._bufferViewLoader);  }  if (defined(this._dracoLoader)) {    resourceCache.unload(this._dracoLoader);  }  this._bufferViewLoader = undefined;  this._dracoLoader = undefined;  this._typedArray = undefined;  this._buffer = undefined;  this._gltf = undefined;};
 |