import Texture from './Texture'; import glenum from './core/glenum'; import util from './core/util'; import mathUtil from './math/util'; import vendor from './core/vendor'; var isPowerOfTwo = mathUtil.isPowerOfTwo; var targetList = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; /** * @constructor clay.TextureCube * @extends clay.Texture * * @example * ... * var mat = new clay.Material({ * shader: clay.shader.library.get('clay.phong', 'environmentMap') * }); * var envMap = new clay.TextureCube(); * envMap.load({ * 'px': 'assets/textures/sky/px.jpg', * 'nx': 'assets/textures/sky/nx.jpg' * 'py': 'assets/textures/sky/py.jpg' * 'ny': 'assets/textures/sky/ny.jpg' * 'pz': 'assets/textures/sky/pz.jpg' * 'nz': 'assets/textures/sky/nz.jpg' * }); * mat.set('environmentMap', envMap); * ... * envMap.success(function () { * // Wait for the sky texture loaded * animation.on('frame', function (frameTime) { * renderer.render(scene, camera); * }); * }); */ var TextureCube = Texture.extend(function () { return /** @lends clay.TextureCube# */{ /** * @type {boolean} * @default false */ // PENDING cubemap should not flipY in default. // flipY: false, /** * @type {Object} * @property {?HTMLImageElement|HTMLCanvasElemnet} px * @property {?HTMLImageElement|HTMLCanvasElemnet} nx * @property {?HTMLImageElement|HTMLCanvasElemnet} py * @property {?HTMLImageElement|HTMLCanvasElemnet} ny * @property {?HTMLImageElement|HTMLCanvasElemnet} pz * @property {?HTMLImageElement|HTMLCanvasElemnet} nz */ image: { px: null, nx: null, py: null, ny: null, pz: null, nz: null }, /** * Pixels data of each side. Will be ignored if images are set. * @type {Object} * @property {?Uint8Array} px * @property {?Uint8Array} nx * @property {?Uint8Array} py * @property {?Uint8Array} ny * @property {?Uint8Array} pz * @property {?Uint8Array} nz */ pixels: { px: null, nx: null, py: null, ny: null, pz: null, nz: null }, /** * @type {Array.} */ mipmaps: [] }; }, { textureType: 'textureCube', update: function (renderer) { var _gl = renderer.gl; _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); this.updateCommon(renderer); var glFormat = this.format; var glType = this.type; _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_S, this.getAvailableWrapS()); _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_T, this.getAvailableWrapT()); _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MAG_FILTER, this.getAvailableMagFilter()); _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MIN_FILTER, this.getAvailableMinFilter()); var anisotropicExt = renderer.getGLExtension('EXT_texture_filter_anisotropic'); if (anisotropicExt && this.anisotropic > 1) { _gl.texParameterf(_gl.TEXTURE_CUBE_MAP, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); } // Fallback to float type if browser don't have half float extension if (glType === 36193) { var halfFloatExt = renderer.getGLExtension('OES_texture_half_float'); if (!halfFloatExt) { glType = glenum.FLOAT; } } if (this.mipmaps.length) { var width = this.width; var height = this.height; for (var i = 0; i < this.mipmaps.length; i++) { var mipmap = this.mipmaps[i]; this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); width /= 2; height /= 2; } } else { this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); if (!this.NPOT && this.useMipmap) { _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); } } _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); }, _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { for (var i = 0; i < 6; i++) { var target = targetList[i]; var img = data.image && data.image[target]; if (img) { _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, glFormat, glType, img); } else { _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, width, height, 0, glFormat, glType, data.pixels && data.pixels[target]); } } }, /** * @param {clay.Renderer} renderer * @memberOf clay.TextureCube.prototype */ generateMipmap: function (renderer) { var _gl = renderer.gl; if (this.useMipmap && !this.NPOT) { _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); } }, bind: function (renderer) { renderer.gl.bindTexture(renderer.gl.TEXTURE_CUBE_MAP, this.getWebGLTexture(renderer)); }, unbind: function (renderer) { renderer.gl.bindTexture(renderer.gl.TEXTURE_CUBE_MAP, null); }, // Overwrite the isPowerOfTwo method isPowerOfTwo: function () { if (this.image.px) { return isPowerOfTwo(this.image.px.width) && isPowerOfTwo(this.image.px.height); } else { return isPowerOfTwo(this.width) && isPowerOfTwo(this.height); } }, isRenderable: function () { if (this.image.px) { return isImageRenderable(this.image.px) && isImageRenderable(this.image.nx) && isImageRenderable(this.image.py) && isImageRenderable(this.image.ny) && isImageRenderable(this.image.pz) && isImageRenderable(this.image.nz); } else { return !!(this.width && this.height); } }, load: function (imageList, crossOrigin) { var loading = 0; var self = this; util.each(imageList, function (src, target){ var image = vendor.createImage(); if (crossOrigin) { image.crossOrigin = crossOrigin; } image.onload = function () { loading --; if (loading === 0){ self.dirty(); self.trigger('success', self); } }; image.onerror = function () { loading --; }; loading++; image.src = src; self.image[target] = image; }); return this; } }); Object.defineProperty(TextureCube.prototype, 'width', { get: function () { if (this.image && this.image.px) { return this.image.px.width; } return this._width; }, set: function (value) { if (this.image && this.image.px) { console.warn('Texture from image can\'t set width'); } else { if (this._width !== value) { this.dirty(); } this._width = value; } } }); Object.defineProperty(TextureCube.prototype, 'height', { get: function () { if (this.image && this.image.px) { return this.image.px.height; } return this._height; }, set: function (value) { if (this.image && this.image.px) { console.warn('Texture from image can\'t set height'); } else { if (this._height !== value) { this.dirty(); } this._height = value; } } }); function isImageRenderable(image) { return image.width > 0 && image.height > 0; } export default TextureCube;