| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 | import Check from "../Core/Check.js";import createGuid from "../Core/createGuid.js";import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import IndexDatatype from "../Core/IndexDatatype.js";import WebGLConstants from "../Core/WebGLConstants.js";import BufferUsage from "./BufferUsage.js";/** * @private */function Buffer(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  //>>includeStart('debug', pragmas.debug);  Check.defined("options.context", options.context);  if (!defined(options.typedArray) && !defined(options.sizeInBytes)) {    throw new DeveloperError(      "Either options.sizeInBytes or options.typedArray is required."    );  }  if (defined(options.typedArray) && defined(options.sizeInBytes)) {    throw new DeveloperError(      "Cannot pass in both options.sizeInBytes and options.typedArray."    );  }  if (defined(options.typedArray)) {    Check.typeOf.object("options.typedArray", options.typedArray);    Check.typeOf.number(      "options.typedArray.byteLength",      options.typedArray.byteLength    );  }  if (!BufferUsage.validate(options.usage)) {    throw new DeveloperError("usage is invalid.");  }  //>>includeEnd('debug');  const gl = options.context._gl;  const bufferTarget = options.bufferTarget;  const typedArray = options.typedArray;  let sizeInBytes = options.sizeInBytes;  const usage = options.usage;  const hasArray = defined(typedArray);  if (hasArray) {    sizeInBytes = typedArray.byteLength;  }  //>>includeStart('debug', pragmas.debug);  Check.typeOf.number.greaterThan("sizeInBytes", sizeInBytes, 0);  //>>includeEnd('debug');  const buffer = gl.createBuffer();  gl.bindBuffer(bufferTarget, buffer);  gl.bufferData(bufferTarget, hasArray ? typedArray : sizeInBytes, usage);  gl.bindBuffer(bufferTarget, null);  this._id = createGuid();  this._gl = gl;  this._webgl2 = options.context._webgl2;  this._bufferTarget = bufferTarget;  this._sizeInBytes = sizeInBytes;  this._usage = usage;  this._buffer = buffer;  this.vertexArrayDestroyable = true;}/** * Creates a vertex buffer, which contains untyped vertex data in GPU-controlled memory. * <br /><br /> * A vertex array defines the actual makeup of a vertex, e.g., positions, normals, texture coordinates, * etc., by interpreting the raw data in one or more vertex buffers. * * @param {object} options An object containing the following properties: * @param {Context} options.context The context in which to create the buffer * @param {ArrayBufferView} [options.typedArray] A typed array containing the data to copy to the buffer. * @param {number} [options.sizeInBytes] A <code>Number</code> defining the size of the buffer in bytes. Required if options.typedArray is not given. * @param {BufferUsage} options.usage Specifies the expected usage pattern of the buffer. On some GL implementations, this can significantly affect performance. See {@link BufferUsage}. * @returns {VertexBuffer} The vertex buffer, ready to be attached to a vertex array. * * @exception {DeveloperError} Must specify either <options.typedArray> or <options.sizeInBytes>, but not both. * @exception {DeveloperError} The buffer size must be greater than zero. * @exception {DeveloperError} Invalid <code>usage</code>. * * * @example * // Example 1. Create a dynamic vertex buffer 16 bytes in size. * const buffer = Buffer.createVertexBuffer({ *     context : context, *     sizeInBytes : 16, *     usage : BufferUsage.DYNAMIC_DRAW * }); * * @example * // Example 2. Create a dynamic vertex buffer from three floating-point values. * // The data copied to the vertex buffer is considered raw bytes until it is * // interpreted as vertices using a vertex array. * const positionBuffer = buffer.createVertexBuffer({ *     context : context, *     typedArray : new Float32Array([0, 0, 0]), *     usage : BufferUsage.STATIC_DRAW * }); * * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenBuffer.xml|glGenBuffer} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindBuffer.xml|glBindBuffer} with <code>ARRAY_BUFFER</code> * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml|glBufferData} with <code>ARRAY_BUFFER</code> */Buffer.createVertexBuffer = function (options) {  //>>includeStart('debug', pragmas.debug);  Check.defined("options.context", options.context);  //>>includeEnd('debug');  return new Buffer({    context: options.context,    bufferTarget: WebGLConstants.ARRAY_BUFFER,    typedArray: options.typedArray,    sizeInBytes: options.sizeInBytes,    usage: options.usage,  });};/** * Creates an index buffer, which contains typed indices in GPU-controlled memory. * <br /><br /> * An index buffer can be attached to a vertex array to select vertices for rendering. * <code>Context.draw</code> can render using the entire index buffer or a subset * of the index buffer defined by an offset and count. * * @param {object} options An object containing the following properties: * @param {Context} options.context The context in which to create the buffer * @param {ArrayBufferView} [options.typedArray] A typed array containing the data to copy to the buffer. * @param {number} [options.sizeInBytes] A <code>Number</code> defining the size of the buffer in bytes. Required if options.typedArray is not given. * @param {BufferUsage} options.usage Specifies the expected usage pattern of the buffer. On some GL implementations, this can significantly affect performance. See {@link BufferUsage}. * @param {IndexDatatype} options.indexDatatype The datatype of indices in the buffer. * @returns {IndexBuffer} The index buffer, ready to be attached to a vertex array. * * @exception {DeveloperError} Must specify either <options.typedArray> or <options.sizeInBytes>, but not both. * @exception {DeveloperError} IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system. Check context.elementIndexUint. * @exception {DeveloperError} The size in bytes must be greater than zero. * @exception {DeveloperError} Invalid <code>usage</code>. * @exception {DeveloperError} Invalid <code>indexDatatype</code>. * * * @example * // Example 1. Create a stream index buffer of unsigned shorts that is * // 16 bytes in size. * const buffer = Buffer.createIndexBuffer({ *     context : context, *     sizeInBytes : 16, *     usage : BufferUsage.STREAM_DRAW, *     indexDatatype : IndexDatatype.UNSIGNED_SHORT * }); * * @example * // Example 2. Create a static index buffer containing three unsigned shorts. * const buffer = Buffer.createIndexBuffer({ *     context : context, *     typedArray : new Uint16Array([0, 1, 2]), *     usage : BufferUsage.STATIC_DRAW, *     indexDatatype : IndexDatatype.UNSIGNED_SHORT * }); * * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenBuffer.xml|glGenBuffer} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindBuffer.xml|glBindBuffer} with <code>ELEMENT_ARRAY_BUFFER</code> * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml|glBufferData} with <code>ELEMENT_ARRAY_BUFFER</code> */Buffer.createIndexBuffer = function (options) {  //>>includeStart('debug', pragmas.debug);  Check.defined("options.context", options.context);  if (!IndexDatatype.validate(options.indexDatatype)) {    throw new DeveloperError("Invalid indexDatatype.");  }  if (    options.indexDatatype === IndexDatatype.UNSIGNED_INT &&    !options.context.elementIndexUint  ) {    throw new DeveloperError(      "IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system.  Check context.elementIndexUint."    );  }  //>>includeEnd('debug');  const context = options.context;  const indexDatatype = options.indexDatatype;  const bytesPerIndex = IndexDatatype.getSizeInBytes(indexDatatype);  const buffer = new Buffer({    context: context,    bufferTarget: WebGLConstants.ELEMENT_ARRAY_BUFFER,    typedArray: options.typedArray,    sizeInBytes: options.sizeInBytes,    usage: options.usage,  });  const numberOfIndices = buffer.sizeInBytes / bytesPerIndex;  Object.defineProperties(buffer, {    indexDatatype: {      get: function () {        return indexDatatype;      },    },    bytesPerIndex: {      get: function () {        return bytesPerIndex;      },    },    numberOfIndices: {      get: function () {        return numberOfIndices;      },    },  });  return buffer;};Object.defineProperties(Buffer.prototype, {  sizeInBytes: {    get: function () {      return this._sizeInBytes;    },  },  usage: {    get: function () {      return this._usage;    },  },});Buffer.prototype._getBuffer = function () {  return this._buffer;};Buffer.prototype.copyFromArrayView = function (arrayView, offsetInBytes) {  offsetInBytes = defaultValue(offsetInBytes, 0);  //>>includeStart('debug', pragmas.debug);  Check.defined("arrayView", arrayView);  Check.typeOf.number.lessThanOrEquals(    "offsetInBytes + arrayView.byteLength",    offsetInBytes + arrayView.byteLength,    this._sizeInBytes  );  //>>includeEnd('debug');  const gl = this._gl;  const target = this._bufferTarget;  gl.bindBuffer(target, this._buffer);  gl.bufferSubData(target, offsetInBytes, arrayView);  gl.bindBuffer(target, null);};Buffer.prototype.copyFromBuffer = function (  readBuffer,  readOffset,  writeOffset,  sizeInBytes) {  //>>includeStart('debug', pragmas.debug);  if (!this._webgl2) {    throw new DeveloperError("A WebGL 2 context is required.");  }  if (!defined(readBuffer)) {    throw new DeveloperError("readBuffer must be defined.");  }  if (!defined(sizeInBytes) || sizeInBytes <= 0) {    throw new DeveloperError(      "sizeInBytes must be defined and be greater than zero."    );  }  if (    !defined(readOffset) ||    readOffset < 0 ||    readOffset + sizeInBytes > readBuffer._sizeInBytes  ) {    throw new DeveloperError(      "readOffset must be greater than or equal to zero and readOffset + sizeInBytes must be less than of equal to readBuffer.sizeInBytes."    );  }  if (    !defined(writeOffset) ||    writeOffset < 0 ||    writeOffset + sizeInBytes > this._sizeInBytes  ) {    throw new DeveloperError(      "writeOffset must be greater than or equal to zero and writeOffset + sizeInBytes must be less than of equal to this.sizeInBytes."    );  }  if (    this._buffer === readBuffer._buffer &&    ((writeOffset >= readOffset && writeOffset < readOffset + sizeInBytes) ||      (readOffset > writeOffset && readOffset < writeOffset + sizeInBytes))  ) {    throw new DeveloperError(      "When readBuffer is equal to this, the ranges [readOffset + sizeInBytes) and [writeOffset, writeOffset + sizeInBytes) must not overlap."    );  }  if (    (this._bufferTarget === WebGLConstants.ELEMENT_ARRAY_BUFFER &&      readBuffer._bufferTarget !== WebGLConstants.ELEMENT_ARRAY_BUFFER) ||    (this._bufferTarget !== WebGLConstants.ELEMENT_ARRAY_BUFFER &&      readBuffer._bufferTarget === WebGLConstants.ELEMENT_ARRAY_BUFFER)  ) {    throw new DeveloperError(      "Can not copy an index buffer into another buffer type."    );  }  //>>includeEnd('debug');  const readTarget = WebGLConstants.COPY_READ_BUFFER;  const writeTarget = WebGLConstants.COPY_WRITE_BUFFER;  const gl = this._gl;  gl.bindBuffer(writeTarget, this._buffer);  gl.bindBuffer(readTarget, readBuffer._buffer);  gl.copyBufferSubData(    readTarget,    writeTarget,    readOffset,    writeOffset,    sizeInBytes  );  gl.bindBuffer(writeTarget, null);  gl.bindBuffer(readTarget, null);};Buffer.prototype.getBufferData = function (  arrayView,  sourceOffset,  destinationOffset,  length) {  sourceOffset = defaultValue(sourceOffset, 0);  destinationOffset = defaultValue(destinationOffset, 0);  //>>includeStart('debug', pragmas.debug);  if (!this._webgl2) {    throw new DeveloperError("A WebGL 2 context is required.");  }  if (!defined(arrayView)) {    throw new DeveloperError("arrayView is required.");  }  let copyLength;  let elementSize;  let arrayLength = arrayView.byteLength;  if (!defined(length)) {    if (defined(arrayLength)) {      copyLength = arrayLength - destinationOffset;      elementSize = 1;    } else {      arrayLength = arrayView.length;      copyLength = arrayLength - destinationOffset;      elementSize = arrayView.BYTES_PER_ELEMENT;    }  } else {    copyLength = length;    if (defined(arrayLength)) {      elementSize = 1;    } else {      arrayLength = arrayView.length;      elementSize = arrayView.BYTES_PER_ELEMENT;    }  }  if (destinationOffset < 0 || destinationOffset > arrayLength) {    throw new DeveloperError(      "destinationOffset must be greater than zero and less than the arrayView length."    );  }  if (destinationOffset + copyLength > arrayLength) {    throw new DeveloperError(      "destinationOffset + length must be less than or equal to the arrayViewLength."    );  }  if (sourceOffset < 0 || sourceOffset > this._sizeInBytes) {    throw new DeveloperError(      "sourceOffset must be greater than zero and less than the buffers size."    );  }  if (sourceOffset + copyLength * elementSize > this._sizeInBytes) {    throw new DeveloperError(      "sourceOffset + length must be less than the buffers size."    );  }  //>>includeEnd('debug');  const gl = this._gl;  const target = WebGLConstants.COPY_READ_BUFFER;  gl.bindBuffer(target, this._buffer);  gl.getBufferSubData(    target,    sourceOffset,    arrayView,    destinationOffset,    length  );  gl.bindBuffer(target, null);};Buffer.prototype.isDestroyed = function () {  return false;};Buffer.prototype.destroy = function () {  this._gl.deleteBuffer(this._buffer);  return destroyObject(this);};export default Buffer;
 |