| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 | import Check from "../Core/Check.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 PixelFormat from "../Core/PixelFormat.js";import ContextLimits from "./ContextLimits.js";import PixelDatatype from "./PixelDatatype.js";function attachTexture(framebuffer, attachment, texture) {  const gl = framebuffer._gl;  gl.framebufferTexture2D(    gl.FRAMEBUFFER,    attachment,    texture._target,    texture._texture,    0  );}function attachRenderbuffer(framebuffer, attachment, renderbuffer) {  const gl = framebuffer._gl;  gl.framebufferRenderbuffer(    gl.FRAMEBUFFER,    attachment,    gl.RENDERBUFFER,    renderbuffer._getRenderbuffer()  );}/** * Creates a framebuffer with optional initial color, depth, and stencil attachments. * Framebuffers are used for render-to-texture effects; they allow us to render to * textures in one pass, and read from it in a later pass. * * @param {Object} options The initial framebuffer attachments as shown in the example below. <code>context</code> is required. The possible properties are <code>colorTextures</code>, <code>colorRenderbuffers</code>, <code>depthTexture</code>, <code>depthRenderbuffer</code>, <code>stencilRenderbuffer</code>, <code>depthStencilTexture</code>, <code>depthStencilRenderbuffer</code>, and <code>destroyAttachments</code>. * * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments. * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment. * @exception {DeveloperError} Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment. * @exception {DeveloperError} Cannot have both a depth and depth-stencil renderbuffer. * @exception {DeveloperError} Cannot have both a stencil and depth-stencil renderbuffer. * @exception {DeveloperError} Cannot have both a depth and stencil renderbuffer. * @exception {DeveloperError} The color-texture pixel-format must be a color format. * @exception {DeveloperError} The depth-texture pixel-format must be DEPTH_COMPONENT. * @exception {DeveloperError} The depth-stencil-texture pixel-format must be DEPTH_STENCIL. * @exception {DeveloperError} The number of color attachments exceeds the number supported. * @exception {DeveloperError} The color-texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension. * @exception {DeveloperError} The color-texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions. * * @example * // Create a framebuffer with color and depth texture attachments. * const width = context.canvas.clientWidth; * const height = context.canvas.clientHeight; * const framebuffer = new Framebuffer({ *   context : context, *   colorTextures : [new Texture({ *     context : context, *     width : width, *     height : height, *     pixelFormat : PixelFormat.RGBA *   })], *   depthTexture : new Texture({ *     context : context, *     width : width, *     height : height, *     pixelFormat : PixelFormat.DEPTH_COMPONENT, *     pixelDatatype : PixelDatatype.UNSIGNED_SHORT *   }) * }); * * @private * @constructor */function Framebuffer(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const context = options.context;  //>>includeStart('debug', pragmas.debug);  Check.defined("options.context", context);  //>>includeEnd('debug');  const gl = context._gl;  const maximumColorAttachments = ContextLimits.maximumColorAttachments;  this._gl = gl;  this._framebuffer = gl.createFramebuffer();  this._colorTextures = [];  this._colorRenderbuffers = [];  this._activeColorAttachments = [];  this._depthTexture = undefined;  this._depthRenderbuffer = undefined;  this._stencilRenderbuffer = undefined;  this._depthStencilTexture = undefined;  this._depthStencilRenderbuffer = undefined;  /**   * When true, the framebuffer owns its attachments so they will be destroyed when   * {@link Framebuffer#destroy} is called or when a new attachment is assigned   * to an attachment point.   *   * @type {Boolean}   * @default true   *   * @see Framebuffer#destroy   */  this.destroyAttachments = defaultValue(options.destroyAttachments, true);  // Throw if a texture and renderbuffer are attached to the same point.  This won't  // cause a WebGL error (because only one will be attached), but is likely a developer error.  //>>includeStart('debug', pragmas.debug);  if (defined(options.colorTextures) && defined(options.colorRenderbuffers)) {    throw new DeveloperError(      "Cannot have both color texture and color renderbuffer attachments."    );  }  if (defined(options.depthTexture) && defined(options.depthRenderbuffer)) {    throw new DeveloperError(      "Cannot have both a depth texture and depth renderbuffer attachment."    );  }  if (    defined(options.depthStencilTexture) &&    defined(options.depthStencilRenderbuffer)  ) {    throw new DeveloperError(      "Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment."    );  }  //>>includeEnd('debug');  // Avoid errors defined in Section 6.5 of the WebGL spec  const depthAttachment =    defined(options.depthTexture) || defined(options.depthRenderbuffer);  const depthStencilAttachment =    defined(options.depthStencilTexture) ||    defined(options.depthStencilRenderbuffer);  //>>includeStart('debug', pragmas.debug);  if (depthAttachment && depthStencilAttachment) {    throw new DeveloperError(      "Cannot have both a depth and depth-stencil attachment."    );  }  if (defined(options.stencilRenderbuffer) && depthStencilAttachment) {    throw new DeveloperError(      "Cannot have both a stencil and depth-stencil attachment."    );  }  if (depthAttachment && defined(options.stencilRenderbuffer)) {    throw new DeveloperError(      "Cannot have both a depth and stencil attachment."    );  }  //>>includeEnd('debug');  ///////////////////////////////////////////////////////////////////  this._bind();  let texture;  let renderbuffer;  let i;  let length;  let attachmentEnum;  if (defined(options.colorTextures)) {    const textures = options.colorTextures;    length = this._colorTextures.length = this._activeColorAttachments.length =      textures.length;    //>>includeStart('debug', pragmas.debug);    if (length > maximumColorAttachments) {      throw new DeveloperError(        "The number of color attachments exceeds the number supported."      );    }    //>>includeEnd('debug');    for (i = 0; i < length; ++i) {      texture = textures[i];      //>>includeStart('debug', pragmas.debug);      if (!PixelFormat.isColorFormat(texture.pixelFormat)) {        throw new DeveloperError(          "The color-texture pixel-format must be a color format."        );      }      if (        texture.pixelDatatype === PixelDatatype.FLOAT &&        !context.colorBufferFloat      ) {        throw new DeveloperError(          "The color texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions. See Context.colorBufferFloat."        );      }      if (        texture.pixelDatatype === PixelDatatype.HALF_FLOAT &&        !context.colorBufferHalfFloat      ) {        throw new DeveloperError(          "The color texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension. See Context.colorBufferHalfFloat."        );      }      //>>includeEnd('debug');      attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;      attachTexture(this, attachmentEnum, texture);      this._activeColorAttachments[i] = attachmentEnum;      this._colorTextures[i] = texture;    }  }  if (defined(options.colorRenderbuffers)) {    const renderbuffers = options.colorRenderbuffers;    length = this._colorRenderbuffers.length = this._activeColorAttachments.length =      renderbuffers.length;    //>>includeStart('debug', pragmas.debug);    if (length > maximumColorAttachments) {      throw new DeveloperError(        "The number of color attachments exceeds the number supported."      );    }    //>>includeEnd('debug');    for (i = 0; i < length; ++i) {      renderbuffer = renderbuffers[i];      attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;      attachRenderbuffer(this, attachmentEnum, renderbuffer);      this._activeColorAttachments[i] = attachmentEnum;      this._colorRenderbuffers[i] = renderbuffer;    }  }  if (defined(options.depthTexture)) {    texture = options.depthTexture;    //>>includeStart('debug', pragmas.debug);    if (texture.pixelFormat !== PixelFormat.DEPTH_COMPONENT) {      throw new DeveloperError(        "The depth-texture pixel-format must be DEPTH_COMPONENT."      );    }    //>>includeEnd('debug');    attachTexture(this, this._gl.DEPTH_ATTACHMENT, texture);    this._depthTexture = texture;  }  if (defined(options.depthRenderbuffer)) {    renderbuffer = options.depthRenderbuffer;    attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer);    this._depthRenderbuffer = renderbuffer;  }  if (defined(options.stencilRenderbuffer)) {    renderbuffer = options.stencilRenderbuffer;    attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer);    this._stencilRenderbuffer = renderbuffer;  }  if (defined(options.depthStencilTexture)) {    texture = options.depthStencilTexture;    //>>includeStart('debug', pragmas.debug);    if (texture.pixelFormat !== PixelFormat.DEPTH_STENCIL) {      throw new DeveloperError(        "The depth-stencil pixel-format must be DEPTH_STENCIL."      );    }    //>>includeEnd('debug');    attachTexture(this, this._gl.DEPTH_STENCIL_ATTACHMENT, texture);    this._depthStencilTexture = texture;  }  if (defined(options.depthStencilRenderbuffer)) {    renderbuffer = options.depthStencilRenderbuffer;    attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer);    this._depthStencilRenderbuffer = renderbuffer;  }  this._unBind();}Object.defineProperties(Framebuffer.prototype, {  /**   * The status of the framebuffer. If the status is not WebGLConstants.FRAMEBUFFER_COMPLETE,   * a {@link DeveloperError} will be thrown when attempting to render to the framebuffer.   * @memberof Framebuffer.prototype   * @type {Number}   */  status: {    get: function () {      this._bind();      const status = this._gl.checkFramebufferStatus(this._gl.FRAMEBUFFER);      this._unBind();      return status;    },  },  numberOfColorAttachments: {    get: function () {      return this._activeColorAttachments.length;    },  },  depthTexture: {    get: function () {      return this._depthTexture;    },  },  depthRenderbuffer: {    get: function () {      return this._depthRenderbuffer;    },  },  stencilRenderbuffer: {    get: function () {      return this._stencilRenderbuffer;    },  },  depthStencilTexture: {    get: function () {      return this._depthStencilTexture;    },  },  depthStencilRenderbuffer: {    get: function () {      return this._depthStencilRenderbuffer;    },  },  /**   * True if the framebuffer has a depth attachment.  Depth attachments include   * depth and depth-stencil textures, and depth and depth-stencil renderbuffers.  When   * rendering to a framebuffer, a depth attachment is required for the depth test to have effect.   * @memberof Framebuffer.prototype   * @type {Boolean}   */  hasDepthAttachment: {    get: function () {      return !!(        this.depthTexture ||        this.depthRenderbuffer ||        this.depthStencilTexture ||        this.depthStencilRenderbuffer      );    },  },});Framebuffer.prototype._bind = function () {  const gl = this._gl;  gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);};Framebuffer.prototype._unBind = function () {  const gl = this._gl;  gl.bindFramebuffer(gl.FRAMEBUFFER, null);};Framebuffer.prototype.bindDraw = function () {  const gl = this._gl;  gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._framebuffer);};Framebuffer.prototype.bindRead = function () {  const gl = this._gl;  gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._framebuffer);};Framebuffer.prototype._getActiveColorAttachments = function () {  return this._activeColorAttachments;};Framebuffer.prototype.getColorTexture = function (index) {  //>>includeStart('debug', pragmas.debug);  if (!defined(index) || index < 0 || index >= this._colorTextures.length) {    throw new DeveloperError(      "index is required, must be greater than or equal to zero and must be less than the number of color attachments."    );  }  //>>includeEnd('debug');  return this._colorTextures[index];};Framebuffer.prototype.getColorRenderbuffer = function (index) {  //>>includeStart('debug', pragmas.debug);  if (    !defined(index) ||    index < 0 ||    index >= this._colorRenderbuffers.length  ) {    throw new DeveloperError(      "index is required, must be greater than or equal to zero and must be less than the number of color attachments."    );  }  //>>includeEnd('debug');  return this._colorRenderbuffers[index];};Framebuffer.prototype.isDestroyed = function () {  return false;};Framebuffer.prototype.destroy = function () {  if (this.destroyAttachments) {    // If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed.    let i = 0;    const textures = this._colorTextures;    let length = textures.length;    for (; i < length; ++i) {      const texture = textures[i];      if (defined(texture)) {        texture.destroy();      }    }    const renderbuffers = this._colorRenderbuffers;    length = renderbuffers.length;    for (i = 0; i < length; ++i) {      const renderbuffer = renderbuffers[i];      if (defined(renderbuffer)) {        renderbuffer.destroy();      }    }    this._depthTexture = this._depthTexture && this._depthTexture.destroy();    this._depthRenderbuffer =      this._depthRenderbuffer && this._depthRenderbuffer.destroy();    this._stencilRenderbuffer =      this._stencilRenderbuffer && this._stencilRenderbuffer.destroy();    this._depthStencilTexture =      this._depthStencilTexture && this._depthStencilTexture.destroy();    this._depthStencilRenderbuffer =      this._depthStencilRenderbuffer &&      this._depthStencilRenderbuffer.destroy();  }  this._gl.deleteFramebuffer(this._framebuffer);  return destroyObject(this);};export default Framebuffer;
 |