2022-06-25 00:23:03 +08:00

339 lines
10 KiB
JavaScript

import Texture from './texture';
import { enums, glFilter, glTextureFmt } from './enums';
import { isPow2 } from './misc';
export default class TextureCube extends Texture {
/**
* @constructor
* @param {Device} device
* @param {Object} options
* @param {Array} options.images
* @param {Boolean} options.genMipmaps
* @param {Number} options.width
* @param {Number} options.height
* @param {TEXTURE_FMT_*} options.format
* @param {Number} options.anisotropy
* @param {FILTER_*} options.minFilter
* @param {FILTER_*} options.magFilter
* @param {FILTER_*} options.mipFilter
* @param {WRAP_*} options.wrapS
* @param {WRAP_*} options.wrapT
* @param {WRAP_*} options.wrapR
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
constructor(device, options) {
super(device);
let gl = this._device._gl;
this._target = gl.TEXTURE_CUBE_MAP;
this._glID = gl.createTexture();
this.update(options);
}
/**
* @method update
* @param {Object} options
* @param {Array} options.images
* @param {Boolean} options.genMipmaps
* @param {Number} options.width
* @param {Number} options.height
* @param {TEXTURE_FMT_*} options.format
* @param {Number} options.anisotropy
* @param {FILTER_*} options.minFilter
* @param {FILTER_*} options.magFilter
* @param {FILTER_*} options.mipFilter
* @param {WRAP_*} options.wrapS
* @param {WRAP_*} options.wrapT
* @param {WRAP_*} options.wrapR
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
update(options) {
let gl = this._device._gl;
let genMipmaps = this._genMipmaps;
if (options) {
if (options.width !== undefined) {
this._width = options.width;
}
if (options.height !== undefined) {
this._height = options.height;
}
if (options.anisotropy !== undefined) {
this._anisotropy = options.anisotropy;
}
if (options.minFilter !== undefined) {
this._minFilter = options.minFilter;
}
if (options.magFilter !== undefined) {
this._magFilter = options.magFilter;
}
if (options.mipFilter !== undefined) {
this._mipFilter = options.mipFilter;
}
if (options.wrapS !== undefined) {
this._wrapS = options.wrapS;
}
if (options.wrapT !== undefined) {
this._wrapT = options.wrapT;
}
// wrapR available in webgl2
// if (options.wrapR !== undefined) {
// this._wrapR = options.wrapR;
// }
if (options.format !== undefined) {
this._format = options.format;
this._compressed =
(this._format >= enums.TEXTURE_FMT_RGB_DXT1 && this._format <= enums.TEXTURE_FMT_RGBA_PVRTC_4BPPV1) ||
(this._format >= enums.TEXTURE_FMT_RGB_ETC2 && this._format <= enums.TEXTURE_FMT_RGBA_ETC2)
;
}
// check if generate mipmap
if (options.genMipmaps !== undefined) {
this._genMipmaps = options.genMipmaps;
genMipmaps = options.genMipmaps;
}
if (options.images !== undefined) {
if (options.images.length > 1) {
genMipmaps = false;
if (options.width !== options.height) {
console.warn('texture-cube width and height should be identical.');
}
if (options.width >> (options.images.length - 1) !== 1) {
console.error('texture-cube mipmap is invalid. please set mipmap as 1x1, 2x2, 4x4 ... nxn');
}
}
}
}
// NOTE: get pot after this._width, this._height has been assigned.
let pot = isPow2(this._width) && isPow2(this._height);
if (!pot) {
genMipmaps = false;
}
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glID);
if (options.images !== undefined && options.images.length > 0) {
this._setMipmap(options.images, options.flipY, options.premultiplyAlpha);
if (options.images.length > 1) this._genMipmaps = true;
}
if (genMipmaps) {
gl.hint(gl.GENERATE_MIPMAP_HINT, gl.NICEST);
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
this._genMipmaps = true;
}
this._setTexInfo();
this._device._restoreTexture(0);
}
/**
* @method updateSubImage
* @param {Object} options
* @param {Number} options.x
* @param {Number} options.y
* @param {Number} options.width
* @param {Number} options.height
* @param {Number} options.level
* @param {Number} options.faceIndex
* @param {HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ArrayBufferView} options.image
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
updateSubImage(options) {
let gl = this._device._gl;
let glFmt = glTextureFmt(this._format);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glID);
this._setSubImage(glFmt, options);
this._device._restoreTexture(0);
}
/**
* @method updateImage
* @param {Object} options
* @param {Number} options.width
* @param {Number} options.height
* @param {Number} options.level
* @param {Number} options.faceIndex
* @param {HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ArrayBufferView} options.image
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
updateImage(options) {
let gl = this._device._gl;
let glFmt = glTextureFmt(this._format);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glID);
this._setImage(glFmt, options);
this._device._restoreTexture(0);
}
_setSubImage(glFmt, options) {
let gl = this._device._gl;
let flipY = options.flipY;
let premultiplyAlpha = options.premultiplyAlpha;
let faceIndex = options.faceIndex;
let img = options.image;
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
if (img && !ArrayBuffer.isView(img) && !(img instanceof ArrayBuffer)) {
gl.texSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, options.level, options.x, options.y, glFmt.format, glFmt.pixelType, img);
} else {
if (this._compressed) {
gl.compressedTexSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
options.x,
options.y,
options.width,
options.height,
glFmt.format,
img
);
} else {
gl.texSubImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
options.x,
options.y,
options.width,
options.height,
glFmt.format,
glFmt.pixelType,
img
);
}
}
}
_setImage(glFmt, options) {
let gl = this._device._gl;
let flipY = options.flipY;
let premultiplyAlpha = options.premultiplyAlpha;
let faceIndex = options.faceIndex;
let img = options.image;
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
if (img && !ArrayBuffer.isView(img) && !(img instanceof ArrayBuffer)) {
gl.texImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
glFmt.internalFormat,
glFmt.format,
glFmt.pixelType,
img
);
} else {
if (this._compressed) {
gl.compressedTexImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
glFmt.internalFormat,
options.width,
options.height,
0,
img
);
} else {
gl.texImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
glFmt.internalFormat,
options.width,
options.height,
0,
glFmt.format,
glFmt.pixelType,
img
);
}
}
}
// levelImages = [imagePosX, imageNegX, imagePosY, imageNegY, imagePosZ, imageNegz]
// images = [levelImages0, levelImages1, ...]
_setMipmap(images, flipY, premultiplyAlpha) {
let glFmt = glTextureFmt(this._format);
let options = {
width: this._width,
height: this._height,
faceIndex: 0,
flipY: flipY,
premultiplyAlpha: premultiplyAlpha,
level: 0,
image: null
};
for (let i = 0; i < images.length; ++i) {
let levelImages = images[i];
options.level = i;
options.width = this._width >> i;
options.height = this._height >> i;
for (let face = 0; face < 6; ++face) {
options.faceIndex = face;
options.image = levelImages[face];
this._setImage(glFmt, options);
}
}
}
_setTexInfo() {
let gl = this._device._gl;
let pot = isPow2(this._width) && isPow2(this._height);
// WebGL1 doesn't support all wrap modes with NPOT textures
if (!pot && (this._wrapS !== enums.WRAP_CLAMP || this._wrapT !== enums.WRAP_CLAMP)) {
console.warn('WebGL1 doesn\'t support all wrap modes with NPOT textures');
this._wrapS = enums.WRAP_CLAMP;
this._wrapT = enums.WRAP_CLAMP;
}
let mipFilter = this._genMipmaps ? this._mipFilter : -1;
if (!pot && mipFilter !== -1) {
console.warn('NPOT textures do not support mipmap filter');
mipFilter = -1;
}
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, glFilter(gl, this._minFilter, mipFilter));
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, glFilter(gl, this._magFilter, -1));
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, this._wrapS);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, this._wrapT);
// wrapR available in webgl2
// gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, this._wrapR);
let ext = this._device.ext('EXT_texture_filter_anisotropic');
if (ext) {
gl.texParameteri(gl.TEXTURE_CUBE_MAP, ext.TEXTURE_MAX_ANISOTROPY_EXT, this._anisotropy);
}
}
}