@openhps/core
Version:
Open Hybrid Positioning System - Core component
1,004 lines (946 loc) • 63.7 kB
JavaScript
import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, RGBAFormat, DepthFormat, DepthStencilFormat, UnsignedIntType, FloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, UnsignedByteType, NoColorSpace, LinearSRGBColorSpace, NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare, SRGBTransfer, LinearTransfer, UnsignedShortType, UnsignedInt248Type } from '../../constants.js';
import { createElementNS } from '../../utils.js';
import { ColorManagement } from '../../math/ColorManagement.js';
import { Vector2 } from '../../math/Vector2.js';
import { getByteLength } from '../../extras/TextureUtils.js';
function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) {
const multisampledRTTExt = extensions.has('WEBGL_multisampled_render_to_texture') ? extensions.get('WEBGL_multisampled_render_to_texture') : null;
const supportsInvalidateFramebuffer = typeof navigator === 'undefined' ? false : /OculusBrowser/g.test(navigator.userAgent);
const _imageDimensions = new Vector2();
const _videoTextures = new WeakMap();
let _canvas;
const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source
// cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas,
// also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")!
// Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d).
let useOffscreenCanvas = false;
try {
useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'
// eslint-disable-next-line compat/compat
&& new OffscreenCanvas(1, 1).getContext('2d') !== null;
} catch (err) {
// Ignore any errors
}
function createCanvas(width, height) {
// Use OffscreenCanvas when available. Specially needed in web workers
return useOffscreenCanvas ?
// eslint-disable-next-line compat/compat
new OffscreenCanvas(width, height) : createElementNS('canvas');
}
function resizeImage(image, needsNewCanvas, maxSize) {
let scale = 1;
const dimensions = getDimensions(image);
// handle case if texture exceeds max size
if (dimensions.width > maxSize || dimensions.height > maxSize) {
scale = maxSize / Math.max(dimensions.width, dimensions.height);
}
// only perform resize if necessary
if (scale < 1) {
// only perform resize for certain image types
if (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap || typeof VideoFrame !== 'undefined' && image instanceof VideoFrame) {
const width = Math.floor(scale * dimensions.width);
const height = Math.floor(scale * dimensions.height);
if (_canvas === undefined) _canvas = createCanvas(width, height);
// cube textures can't reuse the same canvas
const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas;
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d');
context.drawImage(image, 0, 0, width, height);
console.warn('THREE.WebGLRenderer: Texture has been resized from (' + dimensions.width + 'x' + dimensions.height + ') to (' + width + 'x' + height + ').');
return canvas;
} else {
if ('data' in image) {
console.warn('THREE.WebGLRenderer: Image in DataTexture is too big (' + dimensions.width + 'x' + dimensions.height + ').');
}
return image;
}
}
return image;
}
function textureNeedsGenerateMipmaps(texture) {
return texture.generateMipmaps;
}
function generateMipmap(target) {
_gl.generateMipmap(target);
}
function getTargetType(texture) {
if (texture.isWebGLCubeRenderTarget) return _gl.TEXTURE_CUBE_MAP;
if (texture.isWebGL3DRenderTarget) return _gl.TEXTURE_3D;
if (texture.isWebGLArrayRenderTarget || texture.isCompressedArrayTexture) return _gl.TEXTURE_2D_ARRAY;
return _gl.TEXTURE_2D;
}
function getInternalFormat(internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false) {
if (internalFormatName !== null) {
if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName];
console.warn('THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'');
}
let internalFormat = glFormat;
if (glFormat === _gl.RED) {
if (glType === _gl.FLOAT) internalFormat = _gl.R32F;
if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F;
if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8;
}
if (glFormat === _gl.RED_INTEGER) {
if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8UI;
if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.R16UI;
if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.R32UI;
if (glType === _gl.BYTE) internalFormat = _gl.R8I;
if (glType === _gl.SHORT) internalFormat = _gl.R16I;
if (glType === _gl.INT) internalFormat = _gl.R32I;
}
if (glFormat === _gl.RG) {
if (glType === _gl.FLOAT) internalFormat = _gl.RG32F;
if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F;
if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8;
}
if (glFormat === _gl.RG_INTEGER) {
if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8UI;
if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.RG16UI;
if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.RG32UI;
if (glType === _gl.BYTE) internalFormat = _gl.RG8I;
if (glType === _gl.SHORT) internalFormat = _gl.RG16I;
if (glType === _gl.INT) internalFormat = _gl.RG32I;
}
if (glFormat === _gl.RGB_INTEGER) {
if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RGB8UI;
if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.RGB16UI;
if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.RGB32UI;
if (glType === _gl.BYTE) internalFormat = _gl.RGB8I;
if (glType === _gl.SHORT) internalFormat = _gl.RGB16I;
if (glType === _gl.INT) internalFormat = _gl.RGB32I;
}
if (glFormat === _gl.RGBA_INTEGER) {
if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RGBA8UI;
if (glType === _gl.UNSIGNED_SHORT) internalFormat = _gl.RGBA16UI;
if (glType === _gl.UNSIGNED_INT) internalFormat = _gl.RGBA32UI;
if (glType === _gl.BYTE) internalFormat = _gl.RGBA8I;
if (glType === _gl.SHORT) internalFormat = _gl.RGBA16I;
if (glType === _gl.INT) internalFormat = _gl.RGBA32I;
}
if (glFormat === _gl.RGB) {
if (glType === _gl.UNSIGNED_INT_5_9_9_9_REV) internalFormat = _gl.RGB9_E5;
}
if (glFormat === _gl.RGBA) {
const transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer(colorSpace);
if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F;
if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F;
if (glType === _gl.UNSIGNED_BYTE) internalFormat = transfer === SRGBTransfer ? _gl.SRGB8_ALPHA8 : _gl.RGBA8;
if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4;
if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1;
}
if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) {
extensions.get('EXT_color_buffer_float');
}
return internalFormat;
}
function getInternalDepthFormat(useStencil, depthType) {
let glInternalFormat;
if (useStencil) {
if (depthType === null || depthType === UnsignedIntType || depthType === UnsignedInt248Type) {
glInternalFormat = _gl.DEPTH24_STENCIL8;
} else if (depthType === FloatType) {
glInternalFormat = _gl.DEPTH32F_STENCIL8;
} else if (depthType === UnsignedShortType) {
glInternalFormat = _gl.DEPTH24_STENCIL8;
console.warn('DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.');
}
} else {
if (depthType === null || depthType === UnsignedIntType || depthType === UnsignedInt248Type) {
glInternalFormat = _gl.DEPTH_COMPONENT24;
} else if (depthType === FloatType) {
glInternalFormat = _gl.DEPTH_COMPONENT32F;
} else if (depthType === UnsignedShortType) {
glInternalFormat = _gl.DEPTH_COMPONENT16;
}
}
return glInternalFormat;
}
function getMipLevels(texture, image) {
if (textureNeedsGenerateMipmaps(texture) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) {
return Math.log2(Math.max(image.width, image.height)) + 1;
} else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) {
// user-defined mipmaps
return texture.mipmaps.length;
} else if (texture.isCompressedTexture && Array.isArray(texture.image)) {
return image.mipmaps.length;
} else {
// texture without mipmaps (only base level)
return 1;
}
}
//
function onTextureDispose(event) {
const texture = event.target;
texture.removeEventListener('dispose', onTextureDispose);
deallocateTexture(texture);
if (texture.isVideoTexture) {
_videoTextures.delete(texture);
}
}
function onRenderTargetDispose(event) {
const renderTarget = event.target;
renderTarget.removeEventListener('dispose', onRenderTargetDispose);
deallocateRenderTarget(renderTarget);
}
//
function deallocateTexture(texture) {
const textureProperties = properties.get(texture);
if (textureProperties.__webglInit === undefined) return;
// check if it's necessary to remove the WebGLTexture object
const source = texture.source;
const webglTextures = _sources.get(source);
if (webglTextures) {
const webglTexture = webglTextures[textureProperties.__cacheKey];
webglTexture.usedTimes--;
// the WebGLTexture object is not used anymore, remove it
if (webglTexture.usedTimes === 0) {
deleteTexture(texture);
}
// remove the weak map entry if no WebGLTexture uses the source anymore
if (Object.keys(webglTextures).length === 0) {
_sources.delete(source);
}
}
properties.remove(texture);
}
function deleteTexture(texture) {
const textureProperties = properties.get(texture);
_gl.deleteTexture(textureProperties.__webglTexture);
const source = texture.source;
const webglTextures = _sources.get(source);
delete webglTextures[textureProperties.__cacheKey];
info.memory.textures--;
}
function deallocateRenderTarget(renderTarget) {
const renderTargetProperties = properties.get(renderTarget);
if (renderTarget.depthTexture) {
renderTarget.depthTexture.dispose();
properties.remove(renderTarget.depthTexture);
}
if (renderTarget.isWebGLCubeRenderTarget) {
for (let i = 0; i < 6; i++) {
if (Array.isArray(renderTargetProperties.__webglFramebuffer[i])) {
for (let level = 0; level < renderTargetProperties.__webglFramebuffer[i].length; level++) _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i][level]);
} else {
_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]);
}
if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]);
}
} else {
if (Array.isArray(renderTargetProperties.__webglFramebuffer)) {
for (let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level++) _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[level]);
} else {
_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer);
}
if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer);
if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer);
if (renderTargetProperties.__webglColorRenderbuffer) {
for (let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i++) {
if (renderTargetProperties.__webglColorRenderbuffer[i]) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer[i]);
}
}
if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer);
}
const textures = renderTarget.textures;
for (let i = 0, il = textures.length; i < il; i++) {
const attachmentProperties = properties.get(textures[i]);
if (attachmentProperties.__webglTexture) {
_gl.deleteTexture(attachmentProperties.__webglTexture);
info.memory.textures--;
}
properties.remove(textures[i]);
}
properties.remove(renderTarget);
}
//
let textureUnits = 0;
function resetTextureUnits() {
textureUnits = 0;
}
function allocateTextureUnit() {
const textureUnit = textureUnits;
if (textureUnit >= capabilities.maxTextures) {
console.warn('THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures);
}
textureUnits += 1;
return textureUnit;
}
function getTextureCacheKey(texture) {
const array = [];
array.push(texture.wrapS);
array.push(texture.wrapT);
array.push(texture.wrapR || 0);
array.push(texture.magFilter);
array.push(texture.minFilter);
array.push(texture.anisotropy);
array.push(texture.internalFormat);
array.push(texture.format);
array.push(texture.type);
array.push(texture.generateMipmaps);
array.push(texture.premultiplyAlpha);
array.push(texture.flipY);
array.push(texture.unpackAlignment);
array.push(texture.colorSpace);
return array.join();
}
//
function setTexture2D(texture, slot) {
const textureProperties = properties.get(texture);
if (texture.isVideoTexture) updateVideoTexture(texture);
if (texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version) {
const image = texture.image;
if (image === null) {
console.warn('THREE.WebGLRenderer: Texture marked for update but no image data found.');
} else if (image.complete === false) {
console.warn('THREE.WebGLRenderer: Texture marked for update but image is incomplete');
} else {
uploadTexture(textureProperties, texture, slot);
return;
}
}
state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot);
}
function setTexture2DArray(texture, slot) {
const textureProperties = properties.get(texture);
if (texture.version > 0 && textureProperties.__version !== texture.version) {
uploadTexture(textureProperties, texture, slot);
return;
}
state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot);
}
function setTexture3D(texture, slot) {
const textureProperties = properties.get(texture);
if (texture.version > 0 && textureProperties.__version !== texture.version) {
uploadTexture(textureProperties, texture, slot);
return;
}
state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot);
}
function setTextureCube(texture, slot) {
const textureProperties = properties.get(texture);
if (texture.version > 0 && textureProperties.__version !== texture.version) {
uploadCubeTexture(textureProperties, texture, slot);
return;
}
state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot);
}
const wrappingToGL = {
[RepeatWrapping]: _gl.REPEAT,
[ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE,
[MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT
};
const filterToGL = {
[NearestFilter]: _gl.NEAREST,
[NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST,
[NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR,
[LinearFilter]: _gl.LINEAR,
[LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST,
[LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR
};
const compareToGL = {
[NeverCompare]: _gl.NEVER,
[AlwaysCompare]: _gl.ALWAYS,
[LessCompare]: _gl.LESS,
[LessEqualCompare]: _gl.LEQUAL,
[EqualCompare]: _gl.EQUAL,
[GreaterEqualCompare]: _gl.GEQUAL,
[GreaterCompare]: _gl.GREATER,
[NotEqualCompare]: _gl.NOTEQUAL
};
function setTextureParameters(textureType, texture) {
if (texture.type === FloatType && extensions.has('OES_texture_float_linear') === false && (texture.magFilter === LinearFilter || texture.magFilter === LinearMipmapNearestFilter || texture.magFilter === NearestMipmapLinearFilter || texture.magFilter === LinearMipmapLinearFilter || texture.minFilter === LinearFilter || texture.minFilter === LinearMipmapNearestFilter || texture.minFilter === NearestMipmapLinearFilter || texture.minFilter === LinearMipmapLinearFilter)) {
console.warn('THREE.WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device.');
}
_gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]);
_gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]);
if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) {
_gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]);
}
_gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]);
_gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]);
if (texture.compareFunction) {
_gl.texParameteri(textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE);
_gl.texParameteri(textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[texture.compareFunction]);
}
if (extensions.has('EXT_texture_filter_anisotropic') === true) {
if (texture.magFilter === NearestFilter) return;
if (texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter) return;
if (texture.type === FloatType && extensions.has('OES_texture_float_linear') === false) return; // verify extension
if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) {
const extension = extensions.get('EXT_texture_filter_anisotropic');
_gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy()));
properties.get(texture).__currentAnisotropy = texture.anisotropy;
}
}
}
function initTexture(textureProperties, texture) {
let forceUpload = false;
if (textureProperties.__webglInit === undefined) {
textureProperties.__webglInit = true;
texture.addEventListener('dispose', onTextureDispose);
}
// create Source <-> WebGLTextures mapping if necessary
const source = texture.source;
let webglTextures = _sources.get(source);
if (webglTextures === undefined) {
webglTextures = {};
_sources.set(source, webglTextures);
}
// check if there is already a WebGLTexture object for the given texture parameters
const textureCacheKey = getTextureCacheKey(texture);
if (textureCacheKey !== textureProperties.__cacheKey) {
// if not, create a new instance of WebGLTexture
if (webglTextures[textureCacheKey] === undefined) {
// create new entry
webglTextures[textureCacheKey] = {
texture: _gl.createTexture(),
usedTimes: 0
};
info.memory.textures++;
// when a new instance of WebGLTexture was created, a texture upload is required
// even if the image contents are identical
forceUpload = true;
}
webglTextures[textureCacheKey].usedTimes++;
// every time the texture cache key changes, it's necessary to check if an instance of
// WebGLTexture can be deleted in order to avoid a memory leak.
const webglTexture = webglTextures[textureProperties.__cacheKey];
if (webglTexture !== undefined) {
webglTextures[textureProperties.__cacheKey].usedTimes--;
if (webglTexture.usedTimes === 0) {
deleteTexture(texture);
}
}
// store references to cache key and WebGLTexture object
textureProperties.__cacheKey = textureCacheKey;
textureProperties.__webglTexture = webglTextures[textureCacheKey].texture;
}
return forceUpload;
}
function uploadTexture(textureProperties, texture, slot) {
let textureType = _gl.TEXTURE_2D;
if (texture.isDataArrayTexture || texture.isCompressedArrayTexture) textureType = _gl.TEXTURE_2D_ARRAY;
if (texture.isData3DTexture) textureType = _gl.TEXTURE_3D;
const forceUpload = initTexture(textureProperties, texture);
const source = texture.source;
state.bindTexture(textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot);
const sourceProperties = properties.get(source);
if (source.version !== sourceProperties.__version || forceUpload === true) {
state.activeTexture(_gl.TEXTURE0 + slot);
const workingPrimaries = ColorManagement.getPrimaries(ColorManagement.workingColorSpace);
const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(texture.colorSpace);
const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL;
_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY);
_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha);
_gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment);
_gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion);
let image = resizeImage(texture.image, false, capabilities.maxTextureSize);
image = verifyColorSpace(texture, image);
const glFormat = utils.convert(texture.format, texture.colorSpace);
const glType = utils.convert(texture.type);
let glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture);
setTextureParameters(textureType, texture);
let mipmap;
const mipmaps = texture.mipmaps;
const useTexStorage = texture.isVideoTexture !== true;
const allocateMemory = sourceProperties.__version === undefined || forceUpload === true;
const dataReady = source.dataReady;
const levels = getMipLevels(texture, image);
if (texture.isDepthTexture) {
glInternalFormat = getInternalDepthFormat(texture.format === DepthStencilFormat, texture.type);
//
if (allocateMemory) {
if (useTexStorage) {
state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height);
} else {
state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null);
}
}
} else if (texture.isDataTexture) {
// use manually created mipmaps if available
// if there are no manual mipmaps
// set 0 level mipmap and then use GL to generate other mipmap levels
if (mipmaps.length > 0) {
if (useTexStorage && allocateMemory) {
state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height);
}
for (let i = 0, il = mipmaps.length; i < il; i++) {
mipmap = mipmaps[i];
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data);
}
} else {
state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data);
}
}
texture.generateMipmaps = false;
} else {
if (useTexStorage) {
if (allocateMemory) {
state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height);
}
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data);
}
} else {
state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data);
}
}
} else if (texture.isCompressedTexture) {
if (texture.isCompressedArrayTexture) {
if (useTexStorage && allocateMemory) {
state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, image.depth);
}
for (let i = 0, il = mipmaps.length; i < il; i++) {
mipmap = mipmaps[i];
if (texture.format !== RGBAFormat) {
if (glFormat !== null) {
if (useTexStorage) {
if (dataReady) {
if (texture.layerUpdates.size > 0) {
const layerByteLength = getByteLength(mipmap.width, mipmap.height, texture.format, texture.type);
for (const layerIndex of texture.layerUpdates) {
const layerData = mipmap.data.subarray(layerIndex * layerByteLength / mipmap.data.BYTES_PER_ELEMENT, (layerIndex + 1) * layerByteLength / mipmap.data.BYTES_PER_ELEMENT);
state.compressedTexSubImage3D(_gl.TEXTURE_2D_ARRAY, i, 0, 0, layerIndex, mipmap.width, mipmap.height, 1, glFormat, layerData);
}
texture.clearLayerUpdates();
} else {
state.compressedTexSubImage3D(_gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data);
}
}
} else {
state.compressedTexImage3D(_gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0);
}
} else {
console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()');
}
} else {
if (useTexStorage) {
if (dataReady) {
state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data);
}
} else {
state.texImage3D(_gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data);
}
}
}
} else {
if (useTexStorage && allocateMemory) {
state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height);
}
for (let i = 0, il = mipmaps.length; i < il; i++) {
mipmap = mipmaps[i];
if (texture.format !== RGBAFormat) {
if (glFormat !== null) {
if (useTexStorage) {
if (dataReady) {
state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data);
}
} else {
state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data);
}
} else {
console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()');
}
} else {
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data);
}
} else {
state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data);
}
}
}
}
} else if (texture.isDataArrayTexture) {
if (useTexStorage) {
if (allocateMemory) {
state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth);
}
if (dataReady) {
if (texture.layerUpdates.size > 0) {
const layerByteLength = getByteLength(image.width, image.height, texture.format, texture.type);
for (const layerIndex of texture.layerUpdates) {
const layerData = image.data.subarray(layerIndex * layerByteLength / image.data.BYTES_PER_ELEMENT, (layerIndex + 1) * layerByteLength / image.data.BYTES_PER_ELEMENT);
state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, layerIndex, image.width, image.height, 1, glFormat, glType, layerData);
}
texture.clearLayerUpdates();
} else {
state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data);
}
}
} else {
state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data);
}
} else if (texture.isData3DTexture) {
if (useTexStorage) {
if (allocateMemory) {
state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth);
}
if (dataReady) {
state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data);
}
} else {
state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data);
}
} else if (texture.isFramebufferTexture) {
if (allocateMemory) {
if (useTexStorage) {
state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height);
} else {
let width = image.width,
height = image.height;
for (let i = 0; i < levels; i++) {
state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null);
width >>= 1;
height >>= 1;
}
}
}
} else {
// regular Texture (image, video, canvas)
// use manually created mipmaps if available
// if there are no manual mipmaps
// set 0 level mipmap and then use GL to generate other mipmap levels
if (mipmaps.length > 0) {
if (useTexStorage && allocateMemory) {
const dimensions = getDimensions(mipmaps[0]);
state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, dimensions.width, dimensions.height);
}
for (let i = 0, il = mipmaps.length; i < il; i++) {
mipmap = mipmaps[i];
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap);
}
} else {
state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap);
}
}
texture.generateMipmaps = false;
} else {
if (useTexStorage) {
if (allocateMemory) {
const dimensions = getDimensions(image);
state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, dimensions.width, dimensions.height);
}
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image);
}
} else {
state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image);
}
}
}
if (textureNeedsGenerateMipmaps(texture)) {
generateMipmap(textureType);
}
sourceProperties.__version = source.version;
if (texture.onUpdate) texture.onUpdate(texture);
}
textureProperties.__version = texture.version;
}
function uploadCubeTexture(textureProperties, texture, slot) {
if (texture.image.length !== 6) return;
const forceUpload = initTexture(textureProperties, texture);
const source = texture.source;
state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot);
const sourceProperties = properties.get(source);
if (source.version !== sourceProperties.__version || forceUpload === true) {
state.activeTexture(_gl.TEXTURE0 + slot);
const workingPrimaries = ColorManagement.getPrimaries(ColorManagement.workingColorSpace);
const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(texture.colorSpace);
const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL;
_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY);
_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha);
_gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment);
_gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion);
const isCompressed = texture.isCompressedTexture || texture.image[0].isCompressedTexture;
const isDataTexture = texture.image[0] && texture.image[0].isDataTexture;
const cubeImage = [];
for (let i = 0; i < 6; i++) {
if (!isCompressed && !isDataTexture) {
cubeImage[i] = resizeImage(texture.image[i], true, capabilities.maxCubemapSize);
} else {
cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i];
}
cubeImage[i] = verifyColorSpace(texture, cubeImage[i]);
}
const image = cubeImage[0],
glFormat = utils.convert(texture.format, texture.colorSpace),
glType = utils.convert(texture.type),
glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace);
const useTexStorage = texture.isVideoTexture !== true;
const allocateMemory = sourceProperties.__version === undefined || forceUpload === true;
const dataReady = source.dataReady;
let levels = getMipLevels(texture, image);
setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture);
let mipmaps;
if (isCompressed) {
if (useTexStorage && allocateMemory) {
state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height);
}
for (let i = 0; i < 6; i++) {
mipmaps = cubeImage[i].mipmaps;
for (let j = 0; j < mipmaps.length; j++) {
const mipmap = mipmaps[j];
if (texture.format !== RGBAFormat) {
if (glFormat !== null) {
if (useTexStorage) {
if (dataReady) {
state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data);
}
} else {
state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data);
}
} else {
console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()');
}
} else {
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data);
}
} else {
state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data);
}
}
}
}
} else {
mipmaps = texture.mipmaps;
if (useTexStorage && allocateMemory) {
// TODO: Uniformly handle mipmap definitions
// Normal textures and compressed cube textures define base level + mips with their mipmap array
// Uncompressed cube textures use their mipmap array only for mips (no base level)
if (mipmaps.length > 0) levels++;
const dimensions = getDimensions(cubeImage[0]);
state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, dimensions.width, dimensions.height);
}
for (let i = 0; i < 6; i++) {
if (isDataTexture) {
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data);
}
} else {
state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data);
}
for (let j = 0; j < mipmaps.length; j++) {
const mipmap = mipmaps[j];
const mipmapImage = mipmap.image[i].image;
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data);
}
} else {
state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data);
}
}
} else {
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]);
}
} else {
state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]);
}
for (let j = 0; j < mipmaps.length; j++) {
const mipmap = mipmaps[j];
if (useTexStorage) {
if (dataReady) {
state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]);
}
} else {
state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]);
}
}
}
}
}
if (textureNeedsGenerateMipmaps(texture)) {
// We assume images for cube map have the same size.
generateMipmap(_gl.TEXTURE_CUBE_MAP);
}
sourceProperties.__version = source.version;
if (texture.onUpdate) texture.onUpdate(texture);
}
textureProperties.__version = texture.version;
}
// Render targets
// Setup storage for target texture and bind it to correct framebuffer
function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget, level) {
const glFormat = utils.convert(texture.format, texture.colorSpace);
const glType = utils.convert(texture.type);
const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace);
const renderTargetProperties = properties.get(renderTarget);
const textureProperties = properties.get(texture);
textureProperties.__renderTarget = renderTarget;
if (!renderTargetProperties.__hasExternalTextures) {
const width = Math.max(1, renderTarget.width >> level);
const height = Math.max(1, renderTarget.height >> level);
if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) {
state.texImage3D(textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null);
} else {
state.texImage2D(textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null);
}
}
state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer);
if (useMultisampledRTT(renderTarget)) {
multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, textureProperties.__webglTexture, 0, getRenderTargetSamples(renderTarget));
} else if (textureTarget === _gl.TEXTURE_2D || textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z) {
// see #24753
_gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, textureProperties.__webglTexture, level);
}
state.bindFramebuffer(_gl.FRAMEBUFFER, null);
}
// Setup storage for internal depth/stencil buffers and bind to correct framebuffer
function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) {
_gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer);
if (renderTarget.depthBuffer) {
// retrieve the depth attachment types
const depthTexture = renderTarget.depthTexture;
const depthType = depthTexture && depthTexture.isDepthTexture ? depthTexture.type : null;
const glInternalFormat = getInternalDepthFormat(renderTarget.stencilBuffer, depthType);
const glAttachmentType = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;
// set up the attachment
const samples = getRenderTargetSamples(renderTarget);
const isUseMultisampledRTT = useMultisampledRTT(renderTarget);
if (isUseMultisampledRTT) {
multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height);
} else if (isMultisample) {
_gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height);
} else {
_gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height);
}
_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, glAttachmentType, _gl.RENDERBUFFER, renderbuffer);
} else {
const textures = renderTarget.textures;
for (let i = 0; i < textures.length; i++) {
const texture = textures[i];
const glFormat = utils.convert(texture.format, texture.colorSpace);
const glType = utils.convert(texture.type);
const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace);
const samples = getRenderTargetSamples(renderTarget);
if (isMultisample && useMultisampledRTT(renderTarget) === false) {
_gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height);
} else if (useMultisampledRTT(renderTarget)) {
multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height);
} else {
_gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height);
}
}
}
_gl.bindRenderbuffer(_gl.RENDERBUFFER, null);
}
// Setup resources for a Depth Texture for a FBO (needs an extension)
function setupDepthTexture(framebuffer, renderTarget) {
const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget;
if (isCube) throw new Error('Depth Texture with cube render targets is not supported');
state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer);
if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) {
throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture');
}
const textureProperties = properties.get(renderTarget.depthTexture);
textureProperties.__renderTarget = renderTarget;
// upload an empty depth texture with framebuffer size
if (!textureProperties.__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) {
renderTarget.depthTexture.image.width = renderTarget.width;
renderTarget.depthTexture.image.height = renderTarget.height;
renderTarget.depthTexture.needsUpdate = true;
}
setTexture2D(renderTarget.depthTexture, 0);
const webglDepthTexture = textureProperties.__webglTexture;
const samples = getRenderTargetSamples(renderTarget);
if (renderTarget.depthTexture.format === DepthFormat) {
if (useMultisampledRTT(renderTarget)) {
multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples);
} else {
_gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0);
}
} else if (renderTarget.depthTexture.format === DepthStencilFormat) {
if (useMultisampledRTT(renderTarget)) {
multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples);
} else {
_gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0);
}
} else {
throw new Error('Unknown depthTexture format');
}
}
// Setup GL resources for a non-texture depth buffer
function setupDepthRenderbuffer(renderTarget) {
const renderTargetProperties = properties.get(renderTarget);
const isCube = renderTarget.isWebGLCubeRenderTarget === true;
// if the bound depth texture has changed
if (renderTargetProperties.__boundDepthTexture !== renderTarget.depthTexture) {
// fire the dispose event to get rid of stored state associated with the previously bound depth buffer
const depthTexture = renderTarget.depthTexture;
if (renderTargetProperties.__depthDisposeCallback) {
renderTargetProperties.__depthDisposeCallback();
}
// set up dispose listeners to track when the currently attached buffer is implicitly unbound
if (depthTexture) {
const disposeEvent = () => {
delete renderTargetProperties.__boundDepthTexture;
delete renderTargetProperties.__depthDisposeCallback;
depthTexture.removeEventListener('dispose', disposeEvent);
};
depthTexture.addEventListener('dispose', disposeEvent);
renderTargetProperties.__depthDisposeCallback = disposeEvent;
}
renderTargetProperties.__boundDepthTexture = depthTexture;
}
if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) {
if (isCube) throw new Error('target.depthTexture not supported in Cube render targets');
setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget);
} else {
if (isCube) {
renderTargetProperties.__webglDepthbuffer = [];
for (let i = 0; i < 6; i++) {
state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]);
if (renderTargetProperties.__webglDepthbuffer[i] === undefined) {
renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer();
setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false);
} else {
// attach buffer if it's been created already
const glAttachmentType = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;
const renderbuffer = renderTargetProperties.__webglDepthbuffer[i];
_gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer);
_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, glAttachmentType, _gl.RENDERBUFFER, renderbuffer);
}
}
} else {
state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer);
if (renderTargetProperties.__webglDepthbuffer === undefined) {
renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();
setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false);
} else {
// attach buffer if it's been created already
const glAttachmentType = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;
const renderbuffer = renderTargetProperties.__webglDepthbuffer;
_gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer);
_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, glAttachmentType, _gl.RENDERBUFFER, renderbuffer);
}
}
}
state.bindFramebuffer(_gl.FRAMEBUFFER, null);
}
// rebind framebuffer with external textures
function rebindTextures(renderTarget, colorTexture, depthTexture) {
const renderTargetProperties = properties.get(renderTarget);
if (colorTexture !== undefined) {
setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0);
}
if (depthTexture !== undefined) {
setupDepthRenderbuffer(renderTarget);
}
}
// Set up GL resources for the render target
function setupRenderTarget(renderTarget) {
const texture = renderTarget.texture;
const renderTargetProperties = properties.get(renderTarget);
const textureProperties = propert