@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
378 lines (377 loc) • 17.1 kB
JavaScript
import { InternalTexture } from "../../Materials/Textures/internalTexture.js";
import { Logger } from "../../Misc/logger.js";
import { ThinEngine } from "../thinEngine.js";
import { IsExponentOfTwo } from "../../Misc/tools.functions.js";
ThinEngine.prototype.updateRawTexture = function (texture, data, format, invertY, compression = null, type = 0, useSRGBBuffer = false) {
if (!texture) {
return;
}
// Babylon's internalSizedFomat but gl's texImage2D internalFormat
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type, format, useSRGBBuffer);
// Babylon's internalFormat but gl's texImage2D format
const internalFormat = this._getInternalFormat(format);
const 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;
};
ThinEngine.prototype.createRawTexture = function (data, width, height, format, generateMipMaps, invertY, samplingMode, compression = null, type = 0,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
creationFlags = 0, useSRGBBuffer = false) {
const texture = new InternalTexture(this, 3 /* InternalTextureSource.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;
texture._useSRGBBuffer = this._getUseSRGBBuffer(useSRGBBuffer, !generateMipMaps);
if (!this._doNotHandleContextLost) {
texture._bufferView = data;
}
this.updateRawTexture(texture, data, format, invertY, compression, type, texture._useSRGBBuffer);
this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
// Filters
const 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;
};
ThinEngine.prototype.createRawCubeTexture = function (data, size, format, type, generateMipMaps, invertY, samplingMode, compression = null) {
const gl = this._gl;
const texture = new InternalTexture(this, 8 /* InternalTextureSource.CubeRaw */);
texture.isCube = true;
texture.format = format;
texture.type = type;
if (!this._doNotHandleContextLost) {
texture._bufferViewArray = data;
}
const textureType = this._getWebGLTextureType(type);
let 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 = 1;
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 = 1;
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.");
}
const width = size;
const height = width;
texture.width = width;
texture.height = height;
texture.invertY = invertY;
texture._compression = compression;
// Double check on POT to generate Mips.
const isPot = !this.needPOTTextures || (IsExponentOfTwo(texture.width) && 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);
}
else {
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
const level = 0;
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
if (compression) {
gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, this.getCaps().s3tc[compression], texture.width, texture.height, 0, undefined);
}
else {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, null);
}
}
this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
}
this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture, true);
// Filters
if (data && generateMipMaps) {
this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
}
const 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;
texture.samplingMode = samplingMode;
texture.isReady = true;
return texture;
};
ThinEngine.prototype.updateRawCubeTexture = function (texture, data, format, type, invertY, compression = null, level = 0) {
texture._bufferViewArray = data;
texture.format = format;
texture.type = type;
texture.invertY = invertY;
texture._compression = compression;
const gl = this._gl;
const textureType = this._getWebGLTextureType(type);
let internalFormat = this._getInternalFormat(format);
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
let 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 = 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);
}
}
const isPot = !this.needPOTTextures || (IsExponentOfTwo(texture.width) && 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;
};
ThinEngine.prototype.createRawCubeTextureFromUrl = function (url, scene, size, format, type, noMipmap, callback, mipmapGenerator, onLoad = null, onError = null, samplingMode = 3, invertY = false) {
const gl = this._gl;
const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
scene?.addPendingData(texture);
texture.url = url;
texture.isReady = false;
this._internalTexturesCache.push(texture);
const onerror = (request, exception) => {
scene?.removePendingData(texture);
if (onError && request) {
onError(request.status + " " + request.statusText, exception);
}
};
const internalCallback = (data) => {
// If the texture has been disposed
if (!texture._hardwareTexture) {
return;
}
const width = texture.width;
const faceDataArrays = callback(data);
if (!faceDataArrays) {
return;
}
if (mipmapGenerator) {
const textureType = this._getWebGLTextureType(type);
let internalFormat = this._getInternalFormat(format);
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
let needConversion = false;
if (internalFormat === gl.RGB) {
internalFormat = gl.RGBA;
needConversion = true;
}
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
this._unpackFlipY(false);
const mipData = mipmapGenerator(faceDataArrays);
for (let level = 0; level < mipData.length; level++) {
const mipSize = width >> level;
for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
let mipFaceData = mipData[level][faceIndex];
if (needConversion) {
mipFaceData = 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);
texture.onLoadedObservable.notifyObservers(texture);
texture.onLoadedObservable.clear();
if (onLoad) {
onLoad();
}
};
this._loadFile(url, (data) => {
internalCallback(data);
}, undefined, scene?.offlineProvider, true, onerror);
return texture;
};
/**
* @internal
*/
function ConvertRGBtoRGBATextureData(rgbData, width, height, textureType) {
// Create new RGBA data container.
let rgbaData;
let val1 = 1;
if (textureType === 1) {
rgbaData = new Float32Array(width * height * 4);
}
else if (textureType === 2) {
rgbaData = new Uint16Array(width * height * 4);
val1 = 15360; // 15360 is the encoding of 1 in half float
}
else if (textureType === 7) {
rgbaData = new Uint32Array(width * height * 4);
}
else {
rgbaData = new Uint8Array(width * height * 4);
}
// Convert each pixel.
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const index = (y * width + x) * 3;
const newIndex = (y * width + x) * 4;
// Map Old Value to new value.
rgbaData[newIndex + 0] = rgbData[index + 0];
rgbaData[newIndex + 1] = rgbData[index + 1];
rgbaData[newIndex + 2] = rgbData[index + 2];
// Add fully opaque alpha channel.
rgbaData[newIndex + 3] = val1;
}
}
return rgbaData;
}
/**
* Create a function for createRawTexture3D/createRawTexture2DArray
* @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
* @internal
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
function MakeCreateRawTextureFunction(is3D) {
return function (data, width, height, depth, format, generateMipMaps, invertY, samplingMode, compression = null, textureType = 0) {
const target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
const source = is3D ? 10 /* InternalTextureSource.Raw3D */ : 11 /* InternalTextureSource.Raw2DArray */;
const texture = new InternalTexture(this, source);
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;
if (is3D) {
texture.is3D = true;
}
else {
texture.is2DArray = true;
}
if (!this._doNotHandleContextLost) {
texture._bufferView = data;
}
if (is3D) {
this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
}
else {
this.updateRawTexture2DArray(texture, data, format, invertY, compression, textureType);
}
this._bindTextureDirectly(target, texture, true);
// Filters
const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
this._gl.texParameteri(target, this._gl.TEXTURE_MAG_FILTER, filters.mag);
this._gl.texParameteri(target, this._gl.TEXTURE_MIN_FILTER, filters.min);
if (generateMipMaps) {
this._gl.generateMipmap(target);
}
this._bindTextureDirectly(target, null);
this._internalTexturesCache.push(texture);
return texture;
};
}
ThinEngine.prototype.createRawTexture2DArray = MakeCreateRawTextureFunction(false);
ThinEngine.prototype.createRawTexture3D = MakeCreateRawTextureFunction(true);
/**
* Create a function for updateRawTexture3D/updateRawTexture2DArray
* @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
* @internal
*/
function MakeUpdateRawTextureFunction(is3D) {
return function (texture, data, format, invertY, compression = null, textureType = 0) {
const target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
const internalType = this._getWebGLTextureType(textureType);
const internalFormat = this._getInternalFormat(format);
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(textureType, format);
this._bindTextureDirectly(target, 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(target, 0, this.getCaps().s3tc[compression], texture.width, texture.height, texture.depth, 0, data);
}
else {
this._gl.texImage3D(target, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data);
}
if (texture.generateMipMaps) {
this._gl.generateMipmap(target);
}
this._bindTextureDirectly(target, null);
// this.resetTextureCache();
texture.isReady = true;
};
}
ThinEngine.prototype.updateRawTexture2DArray = MakeUpdateRawTextureFunction(false);
ThinEngine.prototype.updateRawTexture3D = MakeUpdateRawTextureFunction(true);
//# sourceMappingURL=engine.rawTexture.js.map