UNPKG

@itwin/core-frontend

Version:
637 lines • 32.6 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module WebGL */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Texture2DDataUpdater = exports.TextureCubeHandle = exports.ExternalTextureLoader = exports.Texture2DHandle = exports.TextureHandle = exports.Texture = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const ImageUtil_1 = require("../../../common/ImageUtil"); const IModelApp_1 = require("../../../IModelApp"); const GL_1 = require("./GL"); const RenderFlags_1 = require("./RenderFlags"); const System_1 = require("./System"); function computeBytesUsed(width, height, format, dataType) { const bytesPerComponent = GL_1.GL.Texture.DataType.UnsignedByte === dataType ? 1 : 4; let componentsPerPixel = 1; switch (format) { case GL_1.GL.Texture.Format.Rgb: componentsPerPixel = 3; break; case GL_1.GL.Texture.Format.Rgba: componentsPerPixel = 4; break; } return width * height * componentsPerPixel * bytesPerComponent; } /** Associate texture data with a WebGLTexture from a canvas, image, OR a bitmap. */ function loadTexture2DImageData(handle, params, bytes, source) { handle.bytesUsed = undefined !== bytes ? bytes.byteLength : computeBytesUsed(params.width, params.height, params.format, params.dataType); const tex = (0, core_bentley_1.expectDefined)(handle.getHandle()); const gl = System_1.System.instance.context; // Use tightly packed data gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); // Bind the texture object; make sure we do not interfere with other active textures System_1.System.instance.activateTexture2d(RenderFlags_1.TextureUnit.Zero, tex); // Figure out the internal format. For all but WebGL2 float/half-float datatypes it is just same as format. // TODO: probably need to just support internal format types in Texture2DCreateParams. let internalFormat = params.format; const context2 = System_1.System.instance.context; if (GL_1.GL.Texture.Format.Rgba === params.format) { if (GL_1.GL.Texture.DataType.Float === params.dataType) internalFormat = context2.RGBA32F; else if (context2.HALF_FLOAT === params.dataType) internalFormat = context2.RGBA16F; } else if (GL_1.GL.Texture.Format.DepthStencil === params.format) internalFormat = context2.DEPTH24_STENCIL8; // send the texture data if (undefined !== source) { gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, params.format, params.dataType, source); } else { const pixelData = undefined !== bytes ? bytes : null; gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, params.width, params.height, 0, params.format, params.dataType, pixelData); } if (params.useMipMaps) { gl.generateMipmap(gl.TEXTURE_2D); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, params.interpolate ? gl.LINEAR : gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, params.interpolate ? gl.LINEAR : gl.NEAREST); } if (params.anisotropicFilter) { System_1.System.instance.setMaxAnisotropy(params.anisotropicFilter); } gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, params.wrapMode); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, params.wrapMode); System_1.System.instance.bindTexture2d(RenderFlags_1.TextureUnit.Zero, undefined); } function loadTextureFromBytes(handle, params, bytes) { loadTexture2DImageData(handle, params, bytes); } /** Associate cube texture data with a WebGLTexture from an image. */ function loadTextureCubeImageData(handle, params, images) { handle.bytesUsed = computeBytesUsed(params.dim * 6, params.dim, params.format, params.dataType); const tex = (0, core_bentley_1.expectDefined)(handle.getHandle()); const gl = System_1.System.instance.context; // Use tightly packed data gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); // Bind the texture object; make sure we do not interfere with other active textures System_1.System.instance.activateTextureCubeMap(RenderFlags_1.TextureUnit.Zero, tex); const cubeTargets = [GL_1.GL.Texture.Target.CubeMapPositiveX, GL_1.GL.Texture.Target.CubeMapNegativeX, GL_1.GL.Texture.Target.CubeMapPositiveY, GL_1.GL.Texture.Target.CubeMapNegativeY, GL_1.GL.Texture.Target.CubeMapPositiveZ, GL_1.GL.Texture.Target.CubeMapNegativeZ]; for (let i = 0; i < 6; i++) { gl.texImage2D(cubeTargets[i], 0, params.format, params.format, params.dataType, images[i]); } gl.texParameteri(GL_1.GL.Texture.Target.CubeMap, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(GL_1.GL.Texture.Target.CubeMap, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(GL_1.GL.Texture.Target.CubeMap, gl.TEXTURE_WRAP_S, params.wrapMode); gl.texParameteri(GL_1.GL.Texture.Target.CubeMap, gl.TEXTURE_WRAP_T, params.wrapMode); // gl.texParameteri(GL.Texture.Target.CubeMap, gl.TEXTURE_WRAP_R, params.wrapMode); // Unavailable in GLES2 System_1.System.instance.bindTextureCubeMap(RenderFlags_1.TextureUnit.Zero, undefined); } /** Wrapper class for a WebGL texture handle and parameters specific to an individual texture. * @internal */ class Texture extends core_common_1.RenderTexture { texture; ownership; transparency; get bytesUsed() { return this.texture.bytesUsed; } get hasOwner() { return undefined !== this.ownership; } get key() { return typeof this.ownership !== "string" && typeof this.ownership?.key === "string" ? this.ownership.key : undefined; } constructor(params) { super(params.type); this.ownership = params.ownership; this.texture = params.handle; this.transparency = params.handle.format === GL_1.GL.Texture.Format.Rgba ? params.transparency : core_common_1.TextureTransparency.Opaque; } get isDisposed() { return this.texture.isDisposed; } /** Free this object in the WebGL wrapper. */ dispose() { (0, core_bentley_1.dispose)(this.texture); } } exports.Texture = Texture; function getDataType(data) { return data instanceof Float32Array ? GL_1.GL.Texture.DataType.Float : GL_1.GL.Texture.DataType.UnsignedByte; } /** Parameters used internally to define how to create a texture for use with WebGL. */ class Texture2DCreateParams { width; height; format; dataType; wrapMode; loadImageData; useMipMaps; interpolate; anisotropicFilter; dataBytes; constructor(width, height, format, dataType, wrapMode, loadImageData, useMipMaps, interpolate, anisotropicFilter, dataBytes) { this.width = width; this.height = height; this.format = format; this.dataType = dataType; this.wrapMode = wrapMode; this.loadImageData = loadImageData; this.useMipMaps = useMipMaps; this.interpolate = interpolate; this.anisotropicFilter = anisotropicFilter; this.dataBytes = dataBytes; } static createForData(width, height, data, preserveData = false, wrapMode = GL_1.GL.Texture.WrapMode.ClampToEdge, format = GL_1.GL.Texture.Format.Rgba) { const bytes = (preserveData && data instanceof Uint8Array) ? data : undefined; return new Texture2DCreateParams(width, height, format, getDataType(data), wrapMode, (tex, params) => loadTextureFromBytes(tex, params, data), undefined, undefined, undefined, bytes); } static createForImageBuffer(image, type) { const props = this.getImageProperties(type); if (core_common_1.ImageBufferFormat.Rgb === image.format) props.format = GL_1.GL.Texture.Format.Rgb; return new Texture2DCreateParams(image.width, image.height, props.format, GL_1.GL.Texture.DataType.UnsignedByte, props.wrapMode, (tex, params) => loadTextureFromBytes(tex, params, image.data), props.useMipMaps, props.interpolate); } static createForAttachment(width, height, format, dataType) { return new Texture2DCreateParams(width, height, format, dataType, GL_1.GL.Texture.WrapMode.ClampToEdge, (tex, params) => loadTextureFromBytes(tex, params), undefined, undefined); } static createForImage(image, type) { const props = this.getImageProperties(type); let targetWidth = image.naturalWidth; let targetHeight = image.naturalHeight; if (core_common_1.RenderTexture.Type.Glyph === type) { targetWidth = (0, core_common_1.nextHighestPowerOfTwo)(targetWidth); targetHeight = (0, core_common_1.nextHighestPowerOfTwo)(targetHeight); } else if (!System_1.System.instance.supportsNonPowerOf2Textures && (!(0, core_common_1.isPowerOfTwo)(targetWidth) || !(0, core_common_1.isPowerOfTwo)(targetHeight))) { if (GL_1.GL.Texture.WrapMode.ClampToEdge === props.wrapMode) { // NPOT are supported but not mipmaps // Probably on poor hardware so I choose to disable mipmaps for lower memory usage over quality. If quality is required we need to resize the image to a pow of 2. // Above comment is not necessarily true - WebGL doesn't support NPOT mipmapping, only supporting base NPOT caps props.useMipMaps = undefined; } else if (GL_1.GL.Texture.WrapMode.Repeat === props.wrapMode) { targetWidth = (0, core_common_1.nextHighestPowerOfTwo)(targetWidth); targetHeight = (0, core_common_1.nextHighestPowerOfTwo)(targetHeight); } } // Cap texture dimensions to system WebGL capabilities const maxTexSize = System_1.System.instance.maxTextureSize; targetWidth = Math.min(targetWidth, maxTexSize); targetHeight = Math.min(targetHeight, maxTexSize); let element = image; if (targetWidth !== image.naturalWidth || targetHeight !== image.naturalHeight) { // Resize so dimensions are powers-of-two const canvas = document.createElement("canvas"); canvas.width = targetWidth; canvas.height = targetHeight; const context = (0, core_bentley_1.expectNotNull)(canvas.getContext("2d")); context.drawImage(image, 0, 0, canvas.width, canvas.height); element = canvas; } return new Texture2DCreateParams(targetWidth, targetHeight, props.format, GL_1.GL.Texture.DataType.UnsignedByte, props.wrapMode, (tex, params) => loadTexture2DImageData(tex, params, undefined, element), props.useMipMaps, props.interpolate, props.anisotropicFilter); } static createForImageBitmap(image, type) { const props = this.getImageProperties(type); let targetWidth = image.width; let targetHeight = image.height; if (core_common_1.RenderTexture.Type.Glyph === type) { targetWidth = (0, core_common_1.nextHighestPowerOfTwo)(targetWidth); targetHeight = (0, core_common_1.nextHighestPowerOfTwo)(targetHeight); } else if (!System_1.System.instance.supportsNonPowerOf2Textures && (!(0, core_common_1.isPowerOfTwo)(targetWidth) || !(0, core_common_1.isPowerOfTwo)(targetHeight))) { if (GL_1.GL.Texture.WrapMode.ClampToEdge === props.wrapMode) { // NPOT are supported but not mipmaps // Probably on poor hardware so I choose to disable mipmaps for lower memory usage over quality. If quality is required we need to resize the image to a pow of 2. // Above comment is not necessarily true - WebGL doesn't support NPOT mipmapping, only supporting base NPOT caps props.useMipMaps = undefined; } else if (GL_1.GL.Texture.WrapMode.Repeat === props.wrapMode) { targetWidth = (0, core_common_1.nextHighestPowerOfTwo)(targetWidth); targetHeight = (0, core_common_1.nextHighestPowerOfTwo)(targetHeight); } } // If we have to resize, use a canvas let source = image; if (image.width !== targetWidth || image.height !== targetHeight) { const canvas = document.createElement("canvas"); canvas.width = targetWidth; canvas.height = targetHeight; const context = (0, core_bentley_1.expectNotNull)(canvas.getContext("2d")); context.drawImage(image, 0, 0, canvas.width, canvas.height); source = canvas; } return new Texture2DCreateParams(targetWidth, targetHeight, props.format, GL_1.GL.Texture.DataType.UnsignedByte, props.wrapMode, (tex, params) => loadTexture2DImageData(tex, params, undefined, source), props.useMipMaps, props.interpolate, props.anisotropicFilter); } static getImageProperties(type) { const isSky = core_common_1.RenderTexture.Type.SkyBox === type; const isTile = core_common_1.RenderTexture.Type.TileSection === type; const isThematic = core_common_1.RenderTexture.Type.ThematicGradient === type; const isFilteredTile = core_common_1.RenderTexture.Type.FilteredTileSection === type; const maxAnisotropicFilterLevel = 16; const wrapMode = core_common_1.RenderTexture.Type.Normal === type ? GL_1.GL.Texture.WrapMode.Repeat : GL_1.GL.Texture.WrapMode.ClampToEdge; const useMipMaps = (!isSky && !isTile && !isFilteredTile && !isThematic) ? true : undefined; const interpolate = isThematic ? undefined : true; const anisotropicFilter = isFilteredTile ? maxAnisotropicFilterLevel : undefined; // Always use RGBA. RGB is much slower and almost certainly does not actually save any GPU memory. const format = GL_1.GL.Texture.Format.Rgba; return { format, wrapMode, useMipMaps, interpolate, anisotropicFilter }; } static placeholderParams = new Texture2DCreateParams(1, 1, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte, GL_1.GL.Texture.WrapMode.ClampToEdge, (_tex, _params) => undefined); } class TextureCubeCreateParams { dim; format; dataType; wrapMode; loadImageData; constructor(dim, format, dataType, wrapMode, loadImageData) { this.dim = dim; this.format = format; this.dataType = dataType; this.wrapMode = wrapMode; this.loadImageData = loadImageData; } static createForCubeImages(posX, negX, posY, negY, posZ, negZ) { const targetDim = posX.naturalWidth; if (posX.naturalHeight !== targetDim) // Cube texture dimensions must match (width must equal height) return undefined; const images = [posX, negX, posY, negY, posZ, negZ]; for (let i = 1; i < images.length; i++) { // Dimensions of all six sides must match each other if (images[i].naturalWidth !== targetDim || images[i].naturalHeight !== targetDim) return undefined; } return new TextureCubeCreateParams(targetDim, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte, GL_1.GL.Texture.WrapMode.ClampToEdge, (tex, params) => loadTextureCubeImageData(tex, params, images)); } } /** Wraps a WebGLTextureHandle * @internal */ class TextureHandle { _glTexture; _bytesUsed = 0; get bytesUsed() { return this._bytesUsed; } set bytesUsed(bytesUsed) { // assert(0 === this.bytesUsed); this._bytesUsed = bytesUsed; } /** Get the WebGLTexture for this TextureHandle. */ getHandle() { return this._glTexture; } get isDisposed() { return this._glTexture === undefined; } [Symbol.dispose]() { if (!this.isDisposed) { System_1.System.instance.disposeTexture((0, core_bentley_1.expectDefined)(this._glTexture)); this._glTexture = undefined; this.bytesUsed = 0; } } /** Create a 2D texture for use as a color attachment for rendering */ static createForAttachment(width, height, format, dataType) { return Texture2DHandle.createForAttachment(width, height, format, dataType); } /** Create a 2D texture to hold non-image data */ static createForData(width, height, data, wantPreserveData = false, wrapMode = GL_1.GL.Texture.WrapMode.ClampToEdge, format = GL_1.GL.Texture.Format.Rgba) { return Texture2DHandle.createForData(width, height, data, wantPreserveData, wrapMode, format); } /** Create a 2D texture from a bitmap */ static createForImageBuffer(image, type) { return Texture2DHandle.createForImageBuffer(image, type); } /** Create a 2D texture from an HTMLImageElement. */ static createForImage(image, type) { return Texture2DHandle.createForImage(image, type); } /** Create a 2D texture from an ImageBitmap. */ static createForImageBitmap(image, type) { return Texture2DHandle.createForImageBitmap(image, type); } /** Create a cube map texture from six HTMLImageElement objects. */ static createForCubeImages(posX, negX, posY, negY, posZ, negZ) { return TextureCubeHandle.createForCubeImages(posX, negX, posY, negY, posZ, negZ); } static createForElement(id, imodel, type, format, onLoaded) { return Texture2DHandle.createForElement(id, imodel, type, format, onLoaded); } constructor(glTexture) { this._glTexture = glTexture; } /** For debugging purposes, open a new window containing this texture as an image. */ showDebugImage() { const gl = System_1.System.instance.context; const fbo = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, (0, core_bentley_1.expectDefined)(this.getHandle()), 0); if (gl.FRAMEBUFFER_COMPLETE === gl.checkFramebufferStatus(gl.FRAMEBUFFER)) { const w = this.width; const h = this.height; const pixels = new Uint8Array(w * h * 4); gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels); const buffer = core_common_1.ImageBuffer.create(pixels, core_common_1.ImageBufferFormat.Rgba, w); const url = (0, ImageUtil_1.imageBufferToPngDataUrl)(buffer, false); (0, ImageUtil_1.openImageDataUrlInNewWindow)((0, core_bentley_1.expectDefined)(url), "Classifiers"); } gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.deleteFramebuffer(fbo); } } exports.TextureHandle = TextureHandle; /** @internal */ class Texture2DHandle extends TextureHandle { _width; _height; _format; _dataType; _dataBytes; get width() { return this._width; } get height() { return this._height; } get format() { return this._format; } get dataType() { return this._dataType; } get dataBytes() { return this._dataBytes; } /** Bind specified texture handle to specified texture unit. */ static bindTexture(texUnit, glTex) { (0, core_bentley_1.assert)(!(glTex instanceof TextureHandle)); System_1.System.instance.bindTexture2d(texUnit, glTex); } /** Bind the specified texture to a uniform sampler2D */ static bindSampler(uniform, tex, unit) { (0, core_bentley_1.assert)(!(tex instanceof TextureHandle)); this.bindTexture(unit, tex); uniform.setUniform1i(unit - RenderFlags_1.TextureUnit.Zero); } /** Bind texture handle (if available) associated with an instantiation of this class to specified texture unit. */ bind(texUnit) { if (undefined === this._glTexture) return false; Texture2DHandle.bindTexture(texUnit, this._glTexture); return true; } /** Bind this texture to a uniform sampler2D */ bindSampler(uniform, unit) { if (undefined !== this._glTexture) Texture2DHandle.bindSampler(uniform, this._glTexture, unit); } /** Update the 2D texture contents. */ update(updater) { if (0 === this.width || 0 === this.height || undefined === this._dataBytes || 0 === this._dataBytes.length) { (0, core_bentley_1.assert)(false); return false; } if (!updater.modified) return false; return this.replaceTextureData(this._dataBytes); } /** Replace the 2D texture contents. */ replaceTextureData(data) { (0, core_bentley_1.assert)((GL_1.GL.Texture.DataType.Float === this._dataType) === (data instanceof Float32Array)); const tex = (0, core_bentley_1.expectDefined)(this.getHandle()); if (undefined === tex) return false; const gl = System_1.System.instance.context; gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); // Go through System to ensure we don't interfere with currently-bound textures! System_1.System.instance.activateTexture2d(RenderFlags_1.TextureUnit.Zero, tex); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.width, this.height, this._format, this._dataType, data); System_1.System.instance.bindTexture2d(RenderFlags_1.TextureUnit.Zero, undefined); return true; } static create(params) { const glTex = System_1.System.instance.context.createTexture(); return null !== glTex ? new Texture2DHandle(glTex, params) : undefined; } /** Create a texture for use as a color attachment for rendering */ static createForAttachment(width, height, format, dataType) { return this.create(Texture2DCreateParams.createForAttachment(width, height, format, dataType)); } /** Create a texture to hold non-image data */ static createForData(width, height, data, wantPreserveData = false, wrapMode = GL_1.GL.Texture.WrapMode.ClampToEdge, format = GL_1.GL.Texture.Format.Rgba) { return this.create(Texture2DCreateParams.createForData(width, height, data, wantPreserveData, wrapMode, format)); } /** Create a texture from a bitmap */ static createForImageBuffer(image, type) { if (core_common_1.RenderTexture.Type.TileSection !== type && core_common_1.RenderTexture.Type.ThematicGradient !== type) (0, core_bentley_1.assert)((0, core_common_1.isPowerOfTwo)(image.width) && (0, core_common_1.isPowerOfTwo)(image.height), "###TODO: Resize image dimensions to powers-of-two if necessary"); return this.create(Texture2DCreateParams.createForImageBuffer(image, type)); } /** Create a 2D texture from an HTMLImageElement. */ static createForImage(image, type) { return this.create(Texture2DCreateParams.createForImage(image, type)); } /** Create a 2D texture from an ImageBitmap. */ static createForImageBitmap(image, type) { return this.create(Texture2DCreateParams.createForImageBitmap(image, type)); } static _placeHolderTextureData = new Uint8Array([128, 128, 128, 255]); static createForElement(id, imodel, type, format, onLoaded) { // set a placeholder texture while we wait for the external texture to load const handle = this.createForData(1, 1, this._placeHolderTextureData, undefined, undefined, GL_1.GL.Texture.Format.Rgba); if (undefined === handle) return undefined; // kick off loading the texture from the backend ExternalTextureLoader.instance.loadTexture(handle, id, imodel, type, format, onLoaded); return handle; } reload(params) { this._width = params.width; this._height = params.height; this._format = params.format; this._dataType = params.dataType; this._dataBytes = params.dataBytes; params.loadImageData(this, params); } constructor(glTexture, params) { super(glTexture); this._width = params.width; this._height = params.height; this._format = params.format; this._dataType = params.dataType; this._dataBytes = params.dataBytes; params.loadImageData(this, params); } } exports.Texture2DHandle = Texture2DHandle; /** @internal */ class ExternalTextureLoader { static instance = new ExternalTextureLoader(2); onTexturesLoaded = new core_bentley_1.BeEvent(); _maxActiveRequests; _activeRequests = []; _pendingRequests = []; _convertRequests = []; _convertPending = false; get numActiveRequests() { return this._activeRequests.length; } get numPendingRequests() { return this._pendingRequests.length; } get maxActiveRequests() { return this._maxActiveRequests; } constructor(maxActiveRequests) { this._maxActiveRequests = maxActiveRequests; } async _nextRequest(prevReq) { this._activeRequests.splice(this._activeRequests.indexOf(prevReq), 1); if (this._activeRequests.length < this._maxActiveRequests && this._pendingRequests.length > 0) { // length is verified to be > 0, so shift will not return undefined // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const req = this._pendingRequests.shift(); await this._activateRequest(req); } if (this._activeRequests.length < 1 && this._pendingRequests.length < 1) this.onTexturesLoaded.raiseEvent(); } async _activateRequest(req) { if (req.imodel.isClosed) return; this._activeRequests.push(req); try { if (!req.imodel.isClosed) { const maxTextureSize = System_1.System.instance.maxTexSizeAllow; const texData = await req.imodel.queryTextureData({ name: req.name, maxTextureSize }); if (undefined !== texData) { const cnvReq = { req, texData }; this._convertRequests.push(cnvReq); // _convertPending is used to prevent overlapping calls to _convertTexture (from overlapping calls to _activateRequest) // it has been put on the list, so if it doesn't get converted here it will get converted by the loop that is converting the current one do { if (!this._convertPending) await this._convertTexture(); } while (!this._convertPending && this._convertRequests.length > 0); if (!req.imodel.isClosed) { IModelApp_1.IModelApp.tileAdmin.invalidateAllScenes(); if (undefined !== req.onLoaded) req.onLoaded(req, texData); } } } } catch { } return this._nextRequest(req); } async _convertTexture() { this._convertPending = true; try { const cnvReq = this._convertRequests.shift(); if (undefined !== cnvReq) { const imageSource = new core_common_1.ImageSource(cnvReq.texData.bytes, cnvReq.texData.format); if (System_1.System.instance.supportsCreateImageBitmap) { const blob = new Blob([imageSource.data], { type: (0, ImageUtil_1.getImageSourceMimeType)(imageSource.format) }); const image = await createImageBitmap(blob, 0, 0, cnvReq.texData.width, cnvReq.texData.height); if (!cnvReq.req.imodel.isClosed) { cnvReq.req.handle.reload(Texture2DCreateParams.createForImageBitmap(image, cnvReq.req.type)); } } else { const image = await (0, ImageUtil_1.imageElementFromImageSource)(imageSource); if (!cnvReq.req.imodel.isClosed) { cnvReq.req.handle.reload(Texture2DCreateParams.createForImage(image, cnvReq.req.type)); } } } } catch { } this._convertPending = false; } _requestExists(reqToCheck) { for (const r of this._activeRequests) if (reqToCheck.name === r.name && reqToCheck.imodel === r.imodel) return true; for (const r of this._pendingRequests) if (reqToCheck.name === r.name && reqToCheck.imodel === r.imodel) return true; return false; } loadTexture(handle, name, imodel, type, format, onLoaded) { const req = { handle, name, imodel, type, format, onLoaded }; if (this._requestExists(req)) return; if (this._activeRequests.length + 1 > this._maxActiveRequests) { this._pendingRequests.push(req); } else this._activateRequest(req); // eslint-disable-line @typescript-eslint/no-floating-promises } } exports.ExternalTextureLoader = ExternalTextureLoader; /** @internal */ class TextureCubeHandle extends TextureHandle { _dim; // Cubemap texture height and width must match. This must be the same for each of the six faces. _format; // Format must be the same for each of the six faces. _dataType; // Type must be the same for each of the six faces. get width() { return this._dim; } get height() { return this._dim; } get format() { return this._format; } get dataType() { return this._dataType; } get dataBytes() { return undefined; } /** Bind specified cubemap texture handle to specified texture unit. */ static bindTexture(texUnit, glTex) { (0, core_bentley_1.assert)(!(glTex instanceof TextureHandle)); System_1.System.instance.bindTextureCubeMap(texUnit, glTex); } /** Bind the specified texture to a uniform sampler2D */ static bindSampler(uniform, tex, unit) { (0, core_bentley_1.assert)(!(tex instanceof TextureHandle)); this.bindTexture(unit, tex); uniform.setUniform1i(unit - RenderFlags_1.TextureUnit.Zero); } /** Bind texture handle (if available) associated with an instantiation of this class to specified texture unit. */ bind(texUnit) { if (undefined === this._glTexture) return false; TextureCubeHandle.bindTexture(texUnit, this._glTexture); return true; } /** Bind this texture to a uniform sampler2D */ bindSampler(uniform, unit) { if (undefined !== this._glTexture) TextureCubeHandle.bindSampler(uniform, this._glTexture, unit); } static create(params) { const glTex = System_1.System.instance.context.createTexture(); return null !== glTex ? new TextureCubeHandle(glTex, params) : undefined; } /** Create a cube map texture from six HTMLImageElement objects. */ static createForCubeImages(posX, negX, posY, negY, posZ, negZ) { const params = TextureCubeCreateParams.createForCubeImages(posX, negX, posY, negY, posZ, negZ); return params !== undefined ? this.create(params) : undefined; } constructor(glTexture, params) { super(glTexture); this._dim = params.dim; this._format = params.format; this._dataType = params.dataType; params.loadImageData(this, params); } } exports.TextureCubeHandle = TextureCubeHandle; /** @internal */ class Texture2DDataUpdater { data; modified = false; constructor(data) { this.data = data; } setByteAtIndex(index, byte) { (0, core_bentley_1.assert)(index < this.data.length); if (byte !== this.data[index]) { this.data[index] = byte; this.modified = true; } } setOvrFlagsAtIndex(index, value) { (0, core_bentley_1.assert)(index < this.data.length - 1); (0, core_bentley_1.assert)(value < 0xffff); this.setByteAtIndex(index, value & 0xff); this.setByteAtIndex(index + 1, (value & 0xff00) >> 8); } getByteAtIndex(index) { (0, core_bentley_1.assert)(index < this.data.length); return this.data[index]; } getOvrFlagsAtIndex(index) { const lo = this.getByteAtIndex(index); const hi = this.getByteAtIndex(index + 1); return lo | (hi << 8); } } exports.Texture2DDataUpdater = Texture2DDataUpdater; //# sourceMappingURL=Texture.js.map