API Docs for: 0.5.5
Show:

File: kick/texture/Texture.js

define(["kick/core/ProjectAsset", "kick/core/Constants", "kick/core/Util", "kick/math/Vec2", "kick/core/EngineSingleton"],
    function (ProjectAsset, Constants, Util, Vec2, EngineSingleton) {
        "use strict";

        var DEBUG = Constants._DEBUG;

        /**
         * Encapsulate a texture object and its configuration. Note that the texture configuration
         * must be set prior to assigning the texture (using either init, setImage or setImageData).<br>
         *
         * Cubemaps must have dimensions width = height * 6 and the order of the cubemap is
         * positiveX, negativeX, positiveY, negativeY, positiveZ, negativeZ
         * @class Texture
         * @namespace kick.texture
         * @constructor
         * @param {Object} [config]
         * @extends kick.core.ProjectAsset
         */
        return function (config) {
            // extend ProjectAsset
            ProjectAsset(this, config, "kick.texture.Texture");
            if (Constants._ASSERT) {
                if (config === EngineSingleton.engine) {
                    Util.fail("Texture constructor changed - engine parameter is removed");
                }
            }
            var engine = EngineSingleton.engine,
                gl = engine.gl,
                glState = engine.glState,
                texture0 = Constants.GL_TEXTURE0,
                createImageFunction,
                createImageFunctionParameters,
                _textureId = gl.createTexture(),
                _name = "Texture",
                _wrapS =  Constants.GL_REPEAT,
                _wrapT = Constants.GL_REPEAT,
                _minFilter = Constants.GL_LINEAR,
                _magFilter = Constants.GL_LINEAR,
                _generateMipmaps = true,
                _autoRescale = true,
                _isMipMapGenerated = false,
                _hasDataOnGPU = false,
                _dataURI =  "memory://void",
                _flipY =  true,
                _type,
                _intFormat = Constants.GL_RGBA,
                _textureType = Constants.GL_TEXTURE_2D,
                _boundTextureType = null,
                thisObj = this,
                _dimension = Vec2.create(),
                /**
                 * @method recreateTextureIfDifferentType
                 * @private
                 */
                recreateTextureIfDifferentType = function () {
                    if (_boundTextureType !== null && _boundTextureType !== _textureType) {
                        gl.deleteTexture(_textureId);
                        _textureId = gl.createTexture();
                    }
                    _boundTextureType = _textureType;
                },
                createMipmaps = function () {
                    gl.generateMipmap(_textureType);
                    _isMipMapGenerated = true;
                },

                contextLost = function () {
                    gl = null;
                },
                contextRestored = function (newGl) {
                    gl = newGl;
                    _textureId = gl.createTexture();
                    if (createImageFunction) {
                        createImageFunction.apply(thisObj, createImageFunctionParameters);
                    }
                };
            if (DEBUG){
                _textureId.id = window.performance.now();
                console.log("Create texture ", _textureId);
            }

            /**
             * Trigger getImageData if dataURI is defined
             * @method init
             */
            this.init = function () {
                if (_dataURI) {
                    engine.resourceLoader.getImageData(_dataURI, thisObj);
                }
            };

            /**
             * Applies the texture settings
             * @method apply
             */
            this.apply = function () {
                if (DEBUG) {
                    if (!_generateMipmaps) {
                        if (_minFilter !== Constants.GL_NEAREST &&
                                _minFilter !== Constants.GL_LINEAR) {
                            Util.warn("When generateMipmaps is false min filter must be either GL_NEAREST or GL_LINEAR");
                        }
                    }
                }

                thisObj.bind(0); // bind to texture slot 0
                if (_textureType === Constants.GL_TEXTURE_2D) {
                    gl.texParameteri(Constants.GL_TEXTURE_2D, Constants.GL_TEXTURE_WRAP_S, _wrapS);
                    gl.texParameteri(Constants.GL_TEXTURE_2D, Constants.GL_TEXTURE_WRAP_T, _wrapT);
                }
                gl.texParameteri(_textureType, Constants.GL_TEXTURE_MAG_FILTER, _magFilter);
                gl.texParameteri(_textureType, Constants.GL_TEXTURE_MIN_FILTER, _minFilter);
            };

            /**
             * Bind the current texture
             * @method bind
             * @param {Number} textureSlot
             */
            this.bind = function (textureSlot) {
                gl.activeTexture(texture0 + textureSlot);
                gl.bindTexture(_textureType, _textureId);
            };

            /**
             * Deallocate the texture from memory
             * @method destroy
             */
            this.destroy = function () {
                if (_textureId !== null) {
                    gl.deleteTexture(_textureId);
                    _textureId = null;
                    engine.project.removeResourceDescriptor(thisObj.uid);
                }
                createImageFunction = null;
                createImageFunctionParameters = null;
                engine.removeEventListener('contextLost', contextLost);
                engine.removeEventListener('contextRestored', contextRestored);
            };

            /**
             * Verifies the current texture configuration and returns the texture error (if any)
             * @method getTextureError
             * @return {String|null}
             */
            this.getTextureError = function(){
                var isPowerOfTwo = Util.isPowerOfTwo(_dimension[0]) &&
                    Util.isPowerOfTwo(_dimension[1]);
                if (!_autoRescale){
                    if (_generateMipmaps && !isPowerOfTwo){
                        return "Mipmaps is only suported by power of two textures";
                    }

                }
                if (_generateMipmaps && (_type===Constants.GL_FLOAT || _type=== Constants.GL_HALF_FLOAT_OES)){
                    return "Mipmaps not supported for floating point textures";
                }

                return null;
            };

            /**
             * Set texture image based on a image object.<br>
             * The image is automatically resized nearest power of two<br>
             * When a textureType == TEXTURE\_CUBE\_MAP the image needs to be in the following format:
             * <ul>
             *     <li>width = 6*height</li>
             *     <li>Image needs to be ordered: [Right, Left, Top, Bottom, Front, Back] (As in <a href="http://www.cgtextures.com/content.php?action=tutorial&name=cubemaps">NVidia DDS Exporter</a>)</li>
             * </ul>
             * @method setImage
             * @param {Image} imageObj image object to import
             * @param {String} dataURI String representing the image
             */
            this.setImage = function (imageObj, dataURI) {
                if (DEBUG){
                    console.log("Texture.setImage", imageObj, dataURI, _textureId);
                }
                createImageFunction = thisObj.setImage;
                createImageFunctionParameters = arguments;
                var width, height, cubemapOrder, srcWidth, srcHeight, canvas, ctx, i;
                _dataURI = dataURI;
                recreateTextureIfDifferentType();
                thisObj.bind(0); // bind to texture slot 0
                if (_textureType === Constants.GL_TEXTURE_2D) {
                    if (_autoRescale){
                        if (!Util.isPowerOfTwo(imageObj.width) || !Util.isPowerOfTwo(imageObj.height)) {
                            width = Util.nextHighestPowerOfTwo(imageObj.width);
                            height = Util.nextHighestPowerOfTwo(imageObj.height);
                            imageObj = Util.scaleImage(imageObj, width, height);
                        }
                    }
                    if (_flipY) {
                        gl.pixelStorei(Constants.GL_UNPACK_FLIP_Y_WEBGL, true);
                    } else {
                        gl.pixelStorei(Constants.GL_UNPACK_FLIP_Y_WEBGL, false);
                    }
                    gl.pixelStorei(Constants.GL_UNPACK_ALIGNMENT, 1);
                    gl.texImage2D(Constants.GL_TEXTURE_2D, 0, _intFormat, _intFormat, Constants.GL_UNSIGNED_BYTE, imageObj);
                    Vec2.copy(_dimension, [imageObj.width, imageObj.height]);
                } else {
                    cubemapOrder = [
                        Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                        Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                        Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                        Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                        Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                        Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
                    ];
                    srcWidth = imageObj.width / 6;
                    srcHeight = imageObj.height;
                    height = Util.nextHighestPowerOfTwo(imageObj.height);
                    //noinspection JSSuspiciousNameCombination
                    width = height;
                    canvas = document.createElement("canvas");
                    canvas.width = width;
                    canvas.height = height;
                    ctx = canvas.getContext("2d");
                    for (i = 0; i < 6; i++) {
                        ctx.drawImage(imageObj,
                            i * srcWidth, 0, srcWidth, srcHeight,
                            0, 0, width, height);
                        gl.pixelStorei(Constants.GL_UNPACK_FLIP_Y_WEBGL, false);
                        gl.pixelStorei(Constants.GL_UNPACK_ALIGNMENT, 1);
                        gl.texImage2D(cubemapOrder[i], 0, _intFormat, _intFormat, Constants.GL_UNSIGNED_BYTE, canvas);
                    }
                    Vec2.copy(_dimension, [width, height]);
                }
                _type = Constants.GL_UNSIGNED_BYTE;
                thisObj.apply();
                if (_generateMipmaps) {
                    createMipmaps();
                }
                _hasDataOnGPU = true;
                glState.currentMaterial = null; // for material to rebind
            };

            /**
             * Calling this function has the side effect of enabling floating point texture (in available on platform)
             * Use GLState.depthTextureExtension instead
             * @method isFPTexturesSupported
             * @return {Boolean}
             * @deprecated
             */
            this.isFPTexturesSupported = function () {
                var res = gl.isTexFloatEnabled;
                if (typeof res !== 'boolean') {
                    res = gl.getExtension("OES_texture_float"); // this has the side effect of enabling the extension
                    gl.isTexFloatEnabled = res;
                }
                return res;
            };

            /**
             * Updates a subset of the texture
             * Note the type of pixels is assumed to be the same as in setImageData
             * @example
             *     var texture = new kick.texture.Texture();
             *     texture.setImageData(2,2,0,kick.core.Constants.GL_UNSIGNED_BYTE,null);
             *     texture.setSubImageData(0, 0, 1, 1, new Uint8Array([255,255,255,255]));
             * @method setSubImageData
             * @param {Number} xoffset
             * @param {Number} yoffset
             * @param {Number} width
             * @param {Number} height
             * @param {ArrayBufferView} pixels
             */
            this.setSubImageData = function (xoffset, yoffset, width, height, pixels) {
                if (Constants._ASSERT) {
                    if (!_textureType) {
                        Util.fail("Texture.textureType not set");
                        return;
                    }
                    if (!_hasDataOnGPU){
                        Util.fail("Texture.setSubImageData must be called after Texture.setImageData or Texture.setImage");
                    }
                    if (width <=0 || height <=0){
                        Util.fail("Texture.setSubImageData width and height must be larger than 0");
                    }
                    if (xoffset + width > _dimension[0]){
                        Util.fail("Texture.setSubImageData xoffset ("+xoffset+") + width ("+width+") must be less than / equal to texture width ("+_dimension[0]+")");
                    }
                    if (yoffset + height > _dimension[1]){
                        Util.fail("Texture.setSubImageData xoffset ("+yoffset+") + width ("+height+") must be less than / equal to texture width ("+_dimension[1]+")");
                    }
                }
                var i,
                    textureSides = _textureType === Constants.GL_TEXTURE_2D ?
                        [Constants.GL_TEXTURE_2D] :
                        [Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                            Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                            Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                            Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                            Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                            Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z];
                thisObj.bind(0); // bind to texture slot 0
                glState.currentMaterial = null; // for material to rebind
                gl.pixelStorei(Constants.GL_UNPACK_ALIGNMENT, 1);
                for (i = 0; i < textureSides.length; i++) {
                    gl.texSubImage2D(textureSides[i], 0, xoffset, yoffset, width, height, _intFormat, _type, pixels);
                }
            };

            /**
             * Set a image using a raw bytearray in a specified format.
             * GL\_FLOAT / GL\_HALF\_FLOAT\_OES should only be used if extension is supported (See GLState.textureFloatExtension / GLState.textureFloatHalfExtension).
             * If only one of GL\_FLOAT/GL\_HALF\_FLOAT\_OES is supported, then the engine will silently use the supported type.
             * If used on cubemap-texture then all 6 sides of the cube is assigned
             * @example
             *     texture = new kick.texture.Texture();
             *     var data = new Uint8Array([
             *         255,255,255,255, 255,0,0,255,
             *         0,255,0,255, 0,0,255,255
             *     ]);
             *     texture.setImageData(2,2,0,kick.core.Constants.GL_UNSIGNED_BYTE,data);
             * @method setImageData
             * @param {Number} width image width in pixels
             * @param {Number} height image height in pixels
             * @param {Number} border image border in pixels
             * @param {Object} type GL\_FLOAT, GL\_HALF\_FLOAT_OES, GL\_UNSIGNED\_BYTE, GL\_UNSIGNED\_SHORT\_4\_4\_4\_4, GL\_UNSIGNED\_SHORT\_5\_5\_5\_1 or GL\_UNSIGNED\_SHORT\_5\_6\_5
             * @param {ArrayBufferView} pixels array of pixels (may be null)
             * @param {String} dataURI String representing the image
             * @param {Number} [cubemapIndex] The cubemap index (only for cubemaps) [+x,-x,+y,-y,+z,-z]. Default is all cubemaps.
             */
            this.setImageData = function (width, height, border, type, pixels, dataURI, cubemapIndex) {
                if (DEBUG){
                    console.log("Texture.setImageData", width, height, border, type, pixels, dataURI, cubemapIndex, _textureId);
                }
                createImageFunction = thisObj.setImageData;
                createImageFunctionParameters = arguments;
                var textureSides = _textureType === Constants.GL_TEXTURE_2D ?
                            [Constants.GL_TEXTURE_2D] :
                            [Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                                Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                                Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                                Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                                Constants.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                                Constants.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z];
                recreateTextureIfDifferentType();
                if (type === Constants.GL_FLOAT) {
                    if (!glState.depthTextureExtension) {
                        if (glState.textureFloatHalfExtension) {
                            type = Constants.GL_HALF_FLOAT_OES;
                        } else {
                            Util.fail("OES_texture_float unsupported on the platform. Using GL_UNSIGNED_BYTE instead of GL_FLOAT.");
                            type = Constants.GL_UNSIGNED_BYTE;
                        }
                    }
                }
                if (type === Constants.GL_HALF_FLOAT_OES){
                    if (!glState.textureFloatHalfExtension) {
                        if (glState.depthTextureExtension) {
                            type = Constants.GL_FLOAT;
                        } else {
                            Util.fail("OES_texture_half_float unsupported on the platform. Using GL_UNSIGNED_BYTE instead of GL_HALF_FLOAT_OES.");
                            type = Constants.GL_UNSIGNED_BYTE;
                        }
                    }
                }
                if (Constants._ASSERT) {
                    if (type !== Constants.GL_FLOAT &&
                            type !== Constants.GL_UNSIGNED_BYTE &&
                            type !== Constants.GL_UNSIGNED_SHORT_4_4_4_4  &&
                            type !== Constants.GL_UNSIGNED_SHORT_5_5_5_1 &&
                            type !== Constants.GL_UNSIGNED_SHORT_5_6_5) {
                        Util.fail("Texture.setImageData (type) should be either GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1 or GL_UNSIGNED_SHORT_5_6_5");
                    }
                }
                if (!_textureType) {
                    Util.fail("Texture.textureType not set");
                    return;
                }


                Vec2.copy(_dimension, [width, height]);
                _dataURI = dataURI;

                thisObj.bind(0); // bind to texture slot 0
                gl.pixelStorei(Constants.GL_UNPACK_ALIGNMENT, 1);
                if (typeof cubemapIndex !== "undefined" || _textureType === Constants.GL_TEXTURE_2D){
                    gl.texImage2D(textureSides[cubemapIndex || 0], 0, _intFormat, width, height, border, _intFormat, type, pixels);
                } else {
                    for (var i = 0; i < textureSides.length; i++) {
                        gl.texImage2D(textureSides[i], 0, _intFormat, width, height, border, _intFormat, type, pixels);
                    }
                }

                gl.texParameteri(_textureType, Constants.GL_TEXTURE_MAG_FILTER, _magFilter);
                gl.texParameteri(_textureType, Constants.GL_TEXTURE_MIN_FILTER, _minFilter);
                gl.texParameteri(_textureType, Constants.GL_TEXTURE_WRAP_S, _wrapS);
                gl.texParameteri(_textureType, Constants.GL_TEXTURE_WRAP_T, _wrapT);
                _type = type;
                if (_generateMipmaps) {
                    createMipmaps();
                }
                _hasDataOnGPU = true;
                glState.currentMaterial = null; // for material to rebind
            };

            /**
             * Creates a 2x2 temporary image (white)
             * @method setTemporaryTexture
             */
            this.setTemporaryTexture = function () {
                var whiteImage = new Uint8Array([255, 255, 255, 255,
                        255, 255, 255,255,
                        255, 255, 255,255,
                        255, 255, 255,255]),
                    oldIntFormat = _intFormat;
                this.internalFormat = Constants.GL_RGBA;
                this.setImageData(2, 2, 0, Constants.GL_UNSIGNED_BYTE, whiteImage, "kickjs://texture/white/");
                _intFormat = oldIntFormat;
            };

            /**
             * Allows setting the dataURI without reloading the image
             * @method setDataURI
             * @param newValue
             * @param automaticGetTextureData
             */
            this.setDataURI = function (newValue, automaticGetTextureData) {
                if (newValue !== _dataURI) {
                    _dataURI = newValue;
                    if (automaticGetTextureData) {
                        engine.resourceLoader.getImageData(_dataURI, thisObj);
                    }
                }
            };

            Object.defineProperties(this, {
                /**
                 * @property engine
                 * @type kick.core.Engine
                 */
                engine: {
                    value: engine
                },
                /**
                 * @property textureId
                 * @type Number
                 * @protected
                 */
                textureId: {
                    value: _textureId
                },
                /**
                 * @property name
                 * @type String
                 */
                name: {
                    get: function () {
                        return _name;
                    },
                    set: function (newValue) {
                        _name = newValue;
                    }
                },
                /**
                 * Dimension of texture [width,height].<br>
                 * Note for cube maps the size is for one face
                 * @property dimension
                 * @type {vec2}
                 */
                dimension: {
                    get: function () {
                        return _dimension;
                    }
                },
                /**
                 * URI of the texture. This property does not load any texture. To load a texture, set this property and
                 * call the init function (or load the image manually and call the setImage() function).<br>
                 * If texture is not on same server, then the web server must support CORS<br>
                 * See http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
                 * @property dataURI
                 * @type String
                 */
                dataURI: {
                    get: function () {
                        return _dataURI;
                    },
                    set: function (newValue) {
                        thisObj.setDataURI(newValue, true);
                    }
                },
                /**
                 * Texture.wrapS should be either GL\_CLAMP\_TO\_EDGE or GL\_REPEAT<br>
                 * @property wrapS
                 * @type Object
                 * @default GL_REPEAT
                 */
                wrapS: {
                    get: function () {
                        return _wrapS;
                    },
                    set: function (value) {
                        if (Constants._ASSERT) {
                            if (value !== Constants.GL_CLAMP_TO_EDGE && value !== Constants.GL_REPEAT) {
                                Util.fail("Texture.wrapS should be either GL_CLAMP_TO_EDGE or GL_REPEAT");
                            }
                        }
                        _wrapS = value;
                    }
                },
                /**
                 * Texture.wrapT should be either GL\_CLAMP\_TO\_EDGE or GL\_REPEAT<br>
                 * @property wrapT
                 * @type Object
                 * @default GL_REPEAT
                 */
                wrapT: {
                    get: function () {
                        return _wrapT;
                    },
                    set: function (value) {
                        if (Constants._ASSERT) {
                            if (value !== Constants.GL_CLAMP_TO_EDGE && value !== Constants.GL_REPEAT) {
                                Util.fail("Texture.wrapT should be either GL_CLAMP_TO_EDGE or GL_REPEAT");
                            }
                        }
                        _wrapT = value;
                    }
                },
                /**
                 * Texture.minFilter should be either GL\_NEAREST, GL\_LINEAR, GL\_NEAREST\_MIPMAP\_NEAREST, <br>
                 * GL\_LINEAR\_MIPMAP\_NEAREST, GL\_NEAREST\_MIPMAP\_LINEAR, GL\_LINEAR\_MIPMAP\_LINEAR<br>
                 * @property minFilter
                 * @type Object
                 * @default GL_LINEAR
                 */
                minFilter: {
                    get: function () {
                        return _minFilter;
                    },
                    set: function (value) {
                        if (Constants._ASSERT) {
                            if (value !== Constants.GL_NEAREST &&
                                    value !== Constants.GL_LINEAR &&
                                    value !== Constants.GL_NEAREST_MIPMAP_NEAREST &&
                                    value !== Constants.GL_LINEAR_MIPMAP_NEAREST &&
                                    value !== Constants.GL_NEAREST_MIPMAP_LINEAR &&
                                    value !== Constants.GL_LINEAR_MIPMAP_LINEAR) {
                                Util.fail("Texture.minFilter should be either GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR");
                            }
                        }
                        _minFilter = value;
                    }
                },
                /**
                 * Texture.magFilter should be either GL\_NEAREST or GL\_LINEAR. <br>
                 * @property magFilter
                 * @type Object
                 * @default GL_LINEAR
                 */
                magFilter: {
                    get: function () {
                        return _magFilter;
                    },
                    set: function (value) {
                        if (Constants._ASSERT) {
                            if (value !== Constants.GL_NEAREST && value !== Constants.GL_LINEAR) {
                                Util.fail("Texture.magFilter should be either GL_NEAREST or GL_LINEAR");
                            }
                        }
                        _magFilter = value;
                    }
                },
                /**
                 * Autogenerate mipmap levels<br>
                 * When an existing texture (without mipmaps) has generateMipmaps=true, then mipmaps are created instantly.
                 * @property generateMipmaps
                 * @type Boolean
                 * @default true
                 */
                generateMipmaps: {
                    get: function () {
                        return _generateMipmaps;
                    },
                    set: function (value) {
                        if (Constants._ASSERT) {
                            if (typeof value !== 'boolean') {
                                Util.fail("Texture.generateMipmaps was not a boolean");
                            }
                        }
                        _generateMipmaps = value;
                        if (_generateMipmaps && !_isMipMapGenerated && _hasDataOnGPU) {
                            thisObj.bind(0);
                            createMipmaps();
                        }
                    }
                },
                /**
                 * When importing image flip the Y direction of the image
                 * <br>
                 * This property is ignored for cube maps.
                 * @property flipY
                 * @type Boolean
                 * @default true
                 */
                flipY: {
                    get: function () {
                        return _flipY;
                    },
                    set: function (value) {
                        if (Constants._ASSERT) {
                            if (typeof value !== 'boolean') {
                                Util.fail("Texture.flipY was not a boolean");
                            }
                        }
                        _flipY = value;
                    }
                },
                /**
                 * Specifies the internal format of the image (format on GPU)<br>
                 * Must be one of the following:
                 * GL\_ALPHA,
                 * GL\_RGB,
                 * GL\_RGBA,
                 * GL\_LUMINANCE,
                 * GL\_LUMINANCE_ALPHA
                 * @property internalFormat
                 * @type Number
                 * @default GL_RGBA
                 */
                internalFormat: {
                    get: function () {
                        return _intFormat;
                    },
                    set: function (value) {
                        if (value !== Constants.GL_ALPHA &&
                                value !== Constants.GL_RGB  &&
                                value !== Constants.GL_RGBA &&
                                value !== Constants.GL_LUMINANCE &&
                                value !== Constants.GL_LUMINANCE_ALPHA) {
                            Util.fail("Texture.internalFormat should be either GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, or LUMINANCE_ALPHA");
                        }
                        _intFormat = value;
                    }
                },
                autoRescale: {
                    get: function(){
                        return _autoRescale;
                    },
                    set: function(newValue){
                        _autoRescale = newValue;
                    }
                },
                /**
                 * Specifies the texture type<br>
                 * Default is GL\_TEXTURE_2D<br>
                 * Must be one of the following:
                 * GL\_TEXTURE_2D,
                 * GL\_TEXTURE_CUBE_MAP
                 * @property textureType
                 * @type Number
                 * @default GL_TEXTURE_2D
                 */
                textureType: {
                    get: function () {
                        return _textureType;
                    },
                    set: function (value) {
                        if (value !== Constants.GL_TEXTURE_2D && value !== Constants.GL_TEXTURE_CUBE_MAP) {
                            Util.fail("Texture.textureType should be either GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP");
                        }
                        _textureType = value;
                    }
                }
            });

            /**
             * Serializes the data into a JSON object (that can be used as a config parameter in the constructor)<br>
             * Note that the texture data is not serialized in the json format. <br>
             * This means that either setImage() or setImageData() must be called before the texture can be bound<br>
             * @method toJSON
             * @return {Object} config element
             */
            this.toJSON = function () {
                return {
                    uid: thisObj.uid,
                    wrapS: _wrapS,
                    wrapT: _wrapT,
                    minFilter: _minFilter,
                    magFilter: _magFilter,
                    autoRescale: _autoRescale,
                    name: _name,
                    generateMipmaps: _generateMipmaps,
                    flipY: _flipY,
                    internalFormat: _intFormat,
                    textureType: _textureType,
                    dataURI: _dataURI
                };
            };

            this.init = function (config) {
                // apply
                Util.applyConfig(thisObj, config, ["uid", "dataURI"]);
                if (config && config.dataURI) {
                    // set dataURI last to make sure that object is configured before initialization
                    thisObj.dataURI = config.dataURI;
                }
            };
            this.init(config);

            engine.addEventListener('contextLost', contextLost);
            engine.addEventListener('contextRestored', contextRestored);
        };

    }
);