import { Nullable } from "../../types"; import { Engine } from "../../Engines/engine"; import { _TimeToken } from "../../Instrumentation/timeToken"; import { InternalTexture } from '../../Materials/Textures/internalTexture'; import { Logger } from '../../Misc/logger'; import { Tools } from '../../Misc/tools'; import { Scene } from '../../scene'; import { WebRequest } from '../../Misc/webRequest'; import { Constants } from '../constants'; declare module "../../Engines/engine" { export interface Engine { /** * Creates a raw texture * @param data defines the data to store in the texture * @param width defines the width of the texture * @param height defines the height of the texture * @param format defines the format of the data * @param generateMipMaps defines if the engine should generate the mip levels * @param invertY defines if data must be stored with Y axis inverted * @param samplingMode defines the required sampling mode (Texture.NEAREST_SAMPLINGMODE by default) * @param compression defines the compression used (null by default) * @param type defines the type fo the data (Engine.TEXTURETYPE_UNSIGNED_INT by default) * @returns the raw texture inside an InternalTexture */ createRawTexture(data: Nullable, width: number, height: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable, type: number): InternalTexture; /** * Update a raw texture * @param texture defines the texture to update * @param data defines the data to store in the texture * @param format defines the format of the data * @param invertY defines if data must be stored with Y axis inverted */ updateRawTexture(texture: Nullable, data: Nullable, format: number, invertY: boolean): void; /** * Update a raw texture * @param texture defines the texture to update * @param data defines the data to store in the texture * @param format defines the format of the data * @param invertY defines if data must be stored with Y axis inverted * @param compression defines the compression used (null by default) * @param type defines the type fo the data (Engine.TEXTURETYPE_UNSIGNED_INT by default) */ updateRawTexture(texture: Nullable, data: Nullable, format: number, invertY: boolean, compression: Nullable, type: number): void; /** * Creates a new raw cube texture * @param data defines the array of data to use to create each face * @param size defines the size of the textures * @param format defines the format of the data * @param type defines the type of the data (like Engine.TEXTURETYPE_UNSIGNED_INT) * @param generateMipMaps defines if the engine should generate the mip levels * @param invertY defines if data must be stored with Y axis inverted * @param samplingMode defines the required sampling mode (like Texture.NEAREST_SAMPLINGMODE) * @param compression defines the compression used (null by default) * @returns the cube texture as an InternalTexture */ createRawCubeTexture(data: Nullable, size: number, format: number, type: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable): InternalTexture; /** * Update a raw cube texture * @param texture defines the texture to udpdate * @param data defines the data to store * @param format defines the data format * @param type defines the type fo the data (Engine.TEXTURETYPE_UNSIGNED_INT by default) * @param invertY defines if data must be stored with Y axis inverted */ updateRawCubeTexture(texture: InternalTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean): void; /** * Update a raw cube texture * @param texture defines the texture to udpdate * @param data defines the data to store * @param format defines the data format * @param type defines the type fo the data (Engine.TEXTURETYPE_UNSIGNED_INT by default) * @param invertY defines if data must be stored with Y axis inverted * @param compression defines the compression used (null by default) */ updateRawCubeTexture(texture: InternalTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable): void; /** * Update a raw cube texture * @param texture defines the texture to udpdate * @param data defines the data to store * @param format defines the data format * @param type defines the type fo the data (Engine.TEXTURETYPE_UNSIGNED_INT by default) * @param invertY defines if data must be stored with Y axis inverted * @param compression defines the compression used (null by default) * @param level defines which level of the texture to update */ updateRawCubeTexture(texture: InternalTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable, level: number): void; /** * Creates a new raw cube texture from a specified url * @param url defines the url where the data is located * @param scene defines the current scene * @param size defines the size of the textures * @param format defines the format of the data * @param type defines the type fo the data (like Engine.TEXTURETYPE_UNSIGNED_INT) * @param noMipmap defines if the engine should avoid generating the mip levels * @param callback defines a callback used to extract texture data from loaded data * @param mipmapGenerator defines to provide an optional tool to generate mip levels * @param onLoad defines a callback called when texture is loaded * @param onError defines a callback called if there is an error * @returns the cube texture as an InternalTexture */ createRawCubeTextureFromUrl(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean, callback: (ArrayBuffer: ArrayBuffer) => Nullable, mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>, onLoad: Nullable<() => void>, onError: Nullable<(message?: string, exception?: any) => void>): InternalTexture; /** * Creates a new raw cube texture from a specified url * @param url defines the url where the data is located * @param scene defines the current scene * @param size defines the size of the textures * @param format defines the format of the data * @param type defines the type fo the data (like Engine.TEXTURETYPE_UNSIGNED_INT) * @param noMipmap defines if the engine should avoid generating the mip levels * @param callback defines a callback used to extract texture data from loaded data * @param mipmapGenerator defines to provide an optional tool to generate mip levels * @param onLoad defines a callback called when texture is loaded * @param onError defines a callback called if there is an error * @param samplingMode defines the required sampling mode (like Texture.NEAREST_SAMPLINGMODE) * @param invertY defines if data must be stored with Y axis inverted * @returns the cube texture as an InternalTexture */ createRawCubeTextureFromUrl(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean, callback: (ArrayBuffer: ArrayBuffer) => Nullable, mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>, onLoad: Nullable<() => void>, onError: Nullable<(message?: string, exception?: any) => void>, samplingMode: number, invertY: boolean): InternalTexture; /** * Creates a new raw 3D texture * @param data defines the data used to create the texture * @param width defines the width of the texture * @param height defines the height of the texture * @param depth defines the depth of the texture * @param format defines the format of the texture * @param generateMipMaps defines if the engine must generate mip levels * @param invertY defines if data must be stored with Y axis inverted * @param samplingMode defines the required sampling mode (like Texture.NEAREST_SAMPLINGMODE) * @param compression defines the compressed used (can be null) * @param textureType defines the compressed used (can be null) * @returns a new raw 3D texture (stored in an InternalTexture) */ createRawTexture3D(data: Nullable, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable, textureType: number): InternalTexture; /** * Update a raw 3D texture * @param texture defines the texture to update * @param data defines the data to store * @param format defines the data format * @param invertY defines if data must be stored with Y axis inverted */ updateRawTexture3D(texture: InternalTexture, data: Nullable, format: number, invertY: boolean): void; /** * Update a raw 3D texture * @param texture defines the texture to update * @param data defines the data to store * @param format defines the data format * @param invertY defines if data must be stored with Y axis inverted * @param compression defines the used compression (can be null) * @param textureType defines the texture Type (Engine.TEXTURETYPE_UNSIGNED_INT, Engine.TEXTURETYPE_FLOAT...) */ updateRawTexture3D(texture: InternalTexture, data: Nullable, format: number, invertY: boolean, compression: Nullable, textureType: number): void; } } Engine.prototype.updateRawTexture = function(texture: Nullable, data: Nullable, format: number, invertY: boolean, compression: Nullable = null, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): void { if (!texture) { return; } // Babylon's internalSizedFomat but gl's texImage2D internalFormat var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type, format); // Babylon's internalFormat but gl's texImage2D format var internalFormat = this._getInternalFormat(format); var textureType = this._getWebGLTextureType(type); this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true); this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false)); if (!this._doNotHandleContextLost) { texture._bufferView = data; texture.format = format; texture.type = type; texture.invertY = invertY; texture._compression = compression; } if (texture.width % 4 !== 0) { this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1); } if (compression && data) { this._gl.compressedTexImage2D(this._gl.TEXTURE_2D, 0, (this.getCaps().s3tc)[compression], texture.width, texture.height, 0, data); } else { this._gl.texImage2D(this._gl.TEXTURE_2D, 0, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, data); } if (texture.generateMipMaps) { this._gl.generateMipmap(this._gl.TEXTURE_2D); } this._bindTextureDirectly(this._gl.TEXTURE_2D, null); // this.resetTextureCache(); texture.isReady = true; }; Engine.prototype.createRawTexture = function(data: Nullable, width: number, height: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable = null, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture { var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RAW); texture.baseWidth = width; texture.baseHeight = height; texture.width = width; texture.height = height; texture.format = format; texture.generateMipMaps = generateMipMaps; texture.samplingMode = samplingMode; texture.invertY = invertY; texture._compression = compression; texture.type = type; if (!this._doNotHandleContextLost) { texture._bufferView = data; } this.updateRawTexture(texture, data, format, invertY, compression, type); this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true); // Filters var filters = this._getSamplingParameters(samplingMode, generateMipMaps); this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag); this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min); if (generateMipMaps) { this._gl.generateMipmap(this._gl.TEXTURE_2D); } this._bindTextureDirectly(this._gl.TEXTURE_2D, null); this._internalTexturesCache.push(texture); return texture; }; Engine.prototype.createRawCubeTexture = function(data: Nullable, size: number, format: number, type: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable = null): InternalTexture { var gl = this._gl; var texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBERAW); texture.isCube = true; texture.format = format; texture.type = type; if (!this._doNotHandleContextLost) { texture._bufferViewArray = data; } var textureType = this._getWebGLTextureType(type); var internalFormat = this._getInternalFormat(format); if (internalFormat === gl.RGB) { internalFormat = gl.RGBA; } // Mipmap generation needs a sized internal format that is both color-renderable and texture-filterable if (textureType === gl.FLOAT && !this._caps.textureFloatLinearFiltering) { generateMipMaps = false; samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE; Logger.Warn("Float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively."); } else if (textureType === this._gl.HALF_FLOAT_OES && !this._caps.textureHalfFloatLinearFiltering) { generateMipMaps = false; samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE; Logger.Warn("Half float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively."); } else if (textureType === gl.FLOAT && !this._caps.textureFloatRender) { generateMipMaps = false; Logger.Warn("Render to float textures is not supported. Mipmap generation forced to false."); } else if (textureType === gl.HALF_FLOAT && !this._caps.colorBufferFloat) { generateMipMaps = false; Logger.Warn("Render to half float textures is not supported. Mipmap generation forced to false."); } var width = size; var height = width; texture.width = width; texture.height = height; // Double check on POT to generate Mips. var isPot = !this.needPOTTextures || (Tools.IsExponentOfTwo(texture.width) && Tools.IsExponentOfTwo(texture.height)); if (!isPot) { generateMipMaps = false; } // Upload data if needed. The texture won't be ready until then. if (data) { this.updateRawCubeTexture(texture, data, format, type, invertY, compression); } this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture, true); // Filters if (data && generateMipMaps) { this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP); } var filters = this._getSamplingParameters(samplingMode, generateMipMaps); gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag); gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, filters.min); gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null); texture.generateMipMaps = generateMipMaps; return texture; }; Engine.prototype.updateRawCubeTexture = function(texture: InternalTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable = null, level: number = 0): void { texture._bufferViewArray = data; texture.format = format; texture.type = type; texture.invertY = invertY; texture._compression = compression; var gl = this._gl; var textureType = this._getWebGLTextureType(type); var internalFormat = this._getInternalFormat(format); var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type); var needConversion = false; if (internalFormat === gl.RGB) { internalFormat = gl.RGBA; needConversion = true; } this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true); this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false)); if (texture.width % 4 !== 0) { gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); } // Data are known to be in +X +Y +Z -X -Y -Z for (let faceIndex = 0; faceIndex < 6; faceIndex++) { let faceData = data[faceIndex]; if (compression) { gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, ((this.getCaps().s3tc))[compression], texture.width, texture.height, 0, faceData); } else { if (needConversion) { faceData = this._convertRGBtoRGBATextureData(faceData, texture.width, texture.height, type); } gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, faceData); } } var isPot = !this.needPOTTextures || (Tools.IsExponentOfTwo(texture.width) && Tools.IsExponentOfTwo(texture.height)); if (isPot && texture.generateMipMaps && level === 0) { this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP); } this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null); // this.resetTextureCache(); texture.isReady = true; }; Engine.prototype.createRawCubeTextureFromUrl = function(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean, callback: (ArrayBuffer: ArrayBuffer) => Nullable, mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, invertY: boolean = false): InternalTexture { var gl = this._gl; var texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode); scene._addPendingData(texture); texture.url = url; this._internalTexturesCache.push(texture); var onerror = (request?: WebRequest, exception?: any) => { scene._removePendingData(texture); if (onError && request) { onError(request.status + " " + request.statusText, exception); } }; var internalCallback = (data: any) => { var width = texture.width; var faceDataArrays = callback(data); if (!faceDataArrays) { return; } if (mipmapGenerator) { var textureType = this._getWebGLTextureType(type); var internalFormat = this._getInternalFormat(format); var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type); var needConversion = false; if (internalFormat === gl.RGB) { internalFormat = gl.RGBA; needConversion = true; } this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true); this._unpackFlipY(false); var mipData = mipmapGenerator(faceDataArrays); for (var level = 0; level < mipData.length; level++) { var mipSize = width >> level; for (var faceIndex = 0; faceIndex < 6; faceIndex++) { let mipFaceData = mipData[level][faceIndex]; if (needConversion) { mipFaceData = this._convertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type); } gl.texImage2D(faceIndex, level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData); } } this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null); } else { this.updateRawCubeTexture(texture, faceDataArrays, format, type, invertY); } texture.isReady = true; // this.resetTextureCache(); scene._removePendingData(texture); if (onLoad) { onLoad(); } }; this._loadFile(url, (data) => { internalCallback(data); }, undefined, scene.offlineProvider, true, onerror); return texture; }; Engine.prototype.createRawTexture3D = function(data: Nullable, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture { var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RAW3D); texture.baseWidth = width; texture.baseHeight = height; texture.baseDepth = depth; texture.width = width; texture.height = height; texture.depth = depth; texture.format = format; texture.type = textureType; texture.generateMipMaps = generateMipMaps; texture.samplingMode = samplingMode; texture.is3D = true; if (!this._doNotHandleContextLost) { texture._bufferView = data; } this.updateRawTexture3D(texture, data, format, invertY, compression, textureType); this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true); // Filters var filters = this._getSamplingParameters(samplingMode, generateMipMaps); this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag); this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min); if (generateMipMaps) { this._gl.generateMipmap(this._gl.TEXTURE_3D); } this._bindTextureDirectly(this._gl.TEXTURE_3D, null); this._internalTexturesCache.push(texture); return texture; }; Engine.prototype.updateRawTexture3D = function(texture: InternalTexture, data: Nullable, format: number, invertY: boolean, compression: Nullable = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void { var internalType = this._getWebGLTextureType(textureType); var internalFormat = this._getInternalFormat(format); var internalSizedFomat = this._getRGBABufferInternalSizedFormat(textureType, format); this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true); this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false)); if (!this._doNotHandleContextLost) { texture._bufferView = data; texture.format = format; texture.invertY = invertY; texture._compression = compression; } if (texture.width % 4 !== 0) { this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1); } if (compression && data) { this._gl.compressedTexImage3D(this._gl.TEXTURE_3D, 0, (this.getCaps().s3tc)[compression], texture.width, texture.height, texture.depth, 0, data); } else { this._gl.texImage3D(this._gl.TEXTURE_3D, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data); } if (texture.generateMipMaps) { this._gl.generateMipmap(this._gl.TEXTURE_3D); } this._bindTextureDirectly(this._gl.TEXTURE_3D, null); // this.resetTextureCache(); texture.isReady = true; };