Browse Source

Merge pull request #4745 from sebavan/master

Texture Loader Extraction
David Catuhe 7 years ago
parent
commit
e13ab1edae

+ 54 - 9
Tools/Gulp/config.json

@@ -176,6 +176,7 @@
                 "../../src/babylon.assetContainer.js",
                 "../../src/babylon.assetContainer.js",
                 "../../src/Mesh/babylon.buffer.js",
                 "../../src/Mesh/babylon.buffer.js",
                 "../../src/Mesh/babylon.vertexBuffer.js",
                 "../../src/Mesh/babylon.vertexBuffer.js",
+                "../../src/Materials/Textures/babylon.internalTextureLoader.js",
                 "../../src/Materials/Textures/babylon.internalTextureTracker.js",
                 "../../src/Materials/Textures/babylon.internalTextureTracker.js",
                 "../../src/Materials/Textures/babylon.internalTexture.js",
                 "../../src/Materials/Textures/babylon.internalTexture.js",
                 "../../src/Materials/Textures/babylon.baseTexture.js",
                 "../../src/Materials/Textures/babylon.baseTexture.js",
@@ -480,7 +481,8 @@
                 "../../src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.js"
                 "../../src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
-                "core"
+                "core",
+                "harmonics"
             ],
             ],
             "shaders": [
             "shaders": [
                 "pbr.vertex",
                 "pbr.vertex",
@@ -1018,15 +1020,13 @@
         },
         },
         "hdr": {
         "hdr": {
             "files": [
             "files": [
-                "../../src/Math/babylon.sphericalPolynomial.js",
-                "../../src/Tools/HDR/babylon.cubemapToSphericalPolynomial.js",
-                "../../src/Tools/HDR/babylon.panoramaToCubemap.js",
                 "../../src/Tools/HDR/babylon.hdr.js",
                 "../../src/Tools/HDR/babylon.hdr.js",
-                "../../src/Tools/babylon.environmentTextureTools.js",
-                "../../src/Materials/Textures/babylon.hdrCubeTexture.js"
+                "../../src/Materials/Textures/babylon.hdrCubeTexture.js",
+                "../../src/Tools/HDR/babylon.panoramaToCubemap.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
-                "core"
+                "core",
+                "harmonics"
             ]
             ]
         },
         },
         "csg": {
         "csg": {
@@ -1073,16 +1073,61 @@
                 "picking"
                 "picking"
             ]
             ]
         },
         },
+        "harmonics": {
+            "files": [
+                "../../src/Math/babylon.sphericalPolynomial.js",
+                "../../src/Tools/HDR/babylon.cubemapToSphericalPolynomial.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
         "textureFormats": {
         "textureFormats": {
+            "files": [ ],
+            "dependUpon": [
+                "dds",
+                "tga",
+                "ktx",
+                "env"
+            ]
+        },
+        "dds": {
             "files": [
             "files": [
-                "../../src/Tools/babylon.tga.js",
                 "../../src/Tools/babylon.dds.js",
                 "../../src/Tools/babylon.dds.js",
-                "../../src/Tools/babylon.khronosTextureContainer.js"
+                "../../src/Materials/Textures/Loaders/babylon.ddsTextureLoader.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
                 "core"
                 "core"
             ]
             ]
         },
         },
+        "tga": {
+            "files": [
+                "../../src/Tools/babylon.tga.js",
+                "../../src/Materials/Textures/Loaders/babylon.tgaTextureLoader.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
+        "ktx": {
+            "files": [
+                "../../src/Tools/babylon.khronosTextureContainer.js",
+                "../../src/Materials/Textures/Loaders/babylon.ktxTextureLoader.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
+        "env": {
+            "files": [
+                "../../src/Tools/babylon.environmentTextureTools.js",
+                "../../src/Materials/Textures/Loaders/babylon.envTextureLoader.js"
+            ],
+            "dependUpon": [
+                "core",
+                "harmonics"
+            ]
+        },
         "debug": {
         "debug": {
             "files": [
             "files": [
                 "../../src/Debug/babylon.skeletonViewer.js",
                 "../../src/Debug/babylon.skeletonViewer.js",

+ 84 - 187
src/Engine/babylon.engine.ts

@@ -264,6 +264,11 @@
             }
             }
         }
         }
 
 
+        /**
+         * Hidden
+         */
+        public static _TextureLoaders: IInternalTextureLoader[] = [];
+
         // Const statics
         // Const statics
         /** Defines that alpha blending is disabled */
         /** Defines that alpha blending is disabled */
         public static readonly ALPHA_DISABLE = 0;
         public static readonly ALPHA_DISABLE = 0;
@@ -4050,11 +4055,13 @@
          * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), HTMLImageElement (image format), or a Blob
          * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), HTMLImageElement (image format), or a Blob
          * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
          * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
          * @param format internal format.  Default: RGB when extension is '.jpg' else RGBA.  Ignored for compressed textures
          * @param format internal format.  Default: RGB when extension is '.jpg' else RGBA.  Ignored for compressed textures
+         * @param forcedExtension defines the extension to use to pick the right loader
          * @returns a InternalTexture for assignment back into BABYLON.Texture
          * @returns a InternalTexture for assignment back into BABYLON.Texture
          */
          */
         public createTexture(urlArg: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<Scene>, samplingMode: number = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE,
         public createTexture(urlArg: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<Scene>, samplingMode: number = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE,
             onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
             onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
-            buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null): InternalTexture {
+            buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
+            forcedExtension: Nullable<string> = null): InternalTexture {
             var url = String(urlArg); // assign a new string, so that the original is still available in case of fallback
             var url = String(urlArg); // assign a new string, so that the original is still available in case of fallback
             var fromData = url.substr(0, 5) === "data:";
             var fromData = url.substr(0, 5) === "data:";
             var fromBlob = url.substr(0, 5) === "blob:";
             var fromBlob = url.substr(0, 5) === "blob:";
@@ -4064,15 +4071,18 @@
 
 
             // establish the file extension, if possible
             // establish the file extension, if possible
             var lastDot = url.lastIndexOf('.');
             var lastDot = url.lastIndexOf('.');
-            var extension = (lastDot > 0) ? url.substring(lastDot).toLowerCase() : "";
-            var isDDS = this.getCaps().s3tc && (extension.indexOf(".dds") === 0);
-            var isTGA = (extension.indexOf(".tga") === 0);
+            var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? url.substring(lastDot).toLowerCase() : "");
+
+            let loader: Nullable<IInternalTextureLoader> = null;
+            for (let availableLoader of Engine._TextureLoaders) {
+                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                    loader = availableLoader;
+                    break;
+                }
+            }
 
 
-            // determine if a ktx file should be substituted
-            var isKTX = false;
-            if (this._textureFormatInUse && !isBase64 && !fallback && !buffer) {
-                url = url.substring(0, lastDot) + this._textureFormatInUse;
-                isKTX = true;
+            if (loader) {
+                url = loader.transformUrl(url, this._textureFormatInUse);
             }
             }
 
 
             if (scene) {
             if (scene) {
@@ -4095,79 +4105,54 @@
 
 
             if (!fallback) this._internalTexturesCache.push(texture);
             if (!fallback) this._internalTexturesCache.push(texture);
 
 
-            var onerror = (message?: string, exception?: any) => {
+            let onInternalError = (message?: string, exception?: any) => {
                 if (scene) {
                 if (scene) {
                     scene._removePendingData(texture);
                     scene._removePendingData(texture);
                 }
                 }
 
 
-                if (onLoadObserver && !isKTX) {
-                    //dont remove the observer if its a ktx file, since the fallback createTexture call will require it.
-                    texture.onLoadedObservable.remove(onLoadObserver);
+                let customFallback = false;
+                if (loader) {
+                    const fallbackUrl = loader.getFallbackTextureUrl(url, this._textureFormatInUse);
+                    if (fallbackUrl) {
+                        // Add Back
+                        customFallback = true;
+                        this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                    }
                 }
                 }
 
 
-                // fallback for when compressed file not found to try again.  For instance, etc1 does not have an alpha capable type
-                if (isKTX) {
-                    this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
-                } else if (Tools.UseFallbackTexture) {
-                    this.createTexture(Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                if (!customFallback) {
+                    if (onLoadObserver) {
+                        texture.onLoadedObservable.remove(onLoadObserver);
+                    }
+                    if (Tools.UseFallbackTexture) {
+                        this.createTexture(Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                    }
                 }
                 }
 
 
                 if (onError) {
                 if (onError) {
                     onError(message || "Unknown error", exception);
                     onError(message || "Unknown error", exception);
                 }
                 }
-            };
-
-            var callback: Nullable<(arrayBuffer: any) => void> = null;
+            }
 
 
             // processing for non-image formats
             // processing for non-image formats
-            if (isKTX || isTGA || isDDS) {
-                if (isKTX) {
-                    callback = (data) => {
-                        var ktx = new KhronosTextureContainer(data, 1);
-
-                        this._prepareWebGLTexture(texture, scene, ktx.pixelWidth, ktx.pixelHeight, invertY, false, true, () => {
-                            ktx.uploadLevels(texture, !noMipmap);
-                            return false;
-                        }, samplingMode);
-                    };
-                } else if (isTGA) {
-                    callback = (arrayBuffer) => {
-                        var data = new Uint8Array(arrayBuffer);
-
-                        var header = TGATools.GetTGAHeader(data);
-
-                        this._prepareWebGLTexture(texture, scene, header.width, header.height, invertY, noMipmap, false, () => {
-                            TGATools.UploadContent(texture, data);
-                            return false;
-                        }, samplingMode);
-                    };
-
-                } else if (isDDS) {
-                    callback = (data) => {
-                        var info = DDSTools.GetDDSInfo(data);
-
-                        var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) === 1);
-                        this._prepareWebGLTexture(texture, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, () => {
-                            DDSTools.UploadDDSLevels(this, texture, data, info, loadMipmap, 1);
-                            return false;
-                        }, samplingMode);
-                    };
+            if (loader) {
+                var callback = (data: string | ArrayBuffer) => {
+                    loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => {
+                        this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
+                                done();
+                                return false;
+                            }, 
+                            samplingMode);
+                    });
                 }
                 }
 
 
                 if (!buffer) {
                 if (!buffer) {
-                    this._loadFile(url, data => {
-                        if (callback) {
-                            callback(data);
-                        }
-                    }, undefined, scene ? scene.database : undefined, true, (request?: XMLHttpRequest, exception?: any) => {
-                        onerror("Unable to load " + (request ? request.responseURL : url, exception));
+                    this._loadFile(url, callback, undefined, scene ? scene.database : undefined, true, (request?: XMLHttpRequest, exception?: any) => {
+                        onInternalError("Unable to load " + (request ? request.responseURL : url, exception));
                     });
                     });
                 } else {
                 } else {
-                    if (callback) {
-                        callback(buffer);
-                    }
+                    callback(buffer as ArrayBuffer);
                 }
                 }
-                // image format processing
             } else {
             } else {
                 var onload = (img: HTMLImageElement) => {
                 var onload = (img: HTMLImageElement) => {
                     if (fromBlob && !this._doNotHandleContextLost) {
                     if (fromBlob && !this._doNotHandleContextLost) {
@@ -4231,11 +4216,11 @@
                     if (buffer instanceof HTMLImageElement) {
                     if (buffer instanceof HTMLImageElement) {
                         onload(buffer);
                         onload(buffer);
                     } else {
                     } else {
-                        Tools.LoadImage(url, onload, onerror, scene ? scene.database : null);
+                        Tools.LoadImage(url, onload, onInternalError, scene ? scene.database : null);
                     }
                     }
                 }
                 }
                 else if (typeof buffer === "string" || buffer instanceof ArrayBuffer || buffer instanceof Blob) {
                 else if (typeof buffer === "string" || buffer instanceof ArrayBuffer || buffer instanceof Blob) {
-                    Tools.LoadImage(buffer, onload, onerror, scene ? scene.database : null);
+                    Tools.LoadImage(buffer, onload, onInternalError, scene ? scene.database : null);
                 }
                 }
                 else {
                 else {
                     onload(<HTMLImageElement>buffer);
                     onload(<HTMLImageElement>buffer);
@@ -5501,142 +5486,50 @@
                 texture._files = files;
                 texture._files = files;
             }
             }
 
 
-            var isKTX = false;
-            var isDDS = false;
-            var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse && !fallback) {
-                rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
-                isKTX = true;
-            } else {
-                isDDS = (extension === ".dds");
-                isEnv = (extension === ".env");
+
+            let loader: Nullable<IInternalTextureLoader> = null;
+            for (let availableLoader of Engine._TextureLoaders) {
+                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
+                    loader = availableLoader;
+                    break;
+                }
             }
             }
 
 
-            let onerror = (request?: XMLHttpRequest, exception?: any) => {
-                if(isKTX){
-                    //remove the format appended to the rootUrl in the original createCubeTexture call.
-                    var exp = new RegExp("" + this._textureFormatInUse + "$");
-                    this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+            let onInternalError = (request?: XMLHttpRequest, exception?: any) => {
+                if (loader) {
+                    const fallbackUrl = loader.getFallbackTextureUrl(rootUrl, this._textureFormatInUse);
+                    if (fallbackUrl) {
+                        this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                    }
                 }
                 }
+
                 if (onError && request) {
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                     onError(request.status + " " + request.statusText, exception);
                 }
                 }
             }
             }
 
 
-            if (isKTX) {
-                this._loadFile(rootUrl, data => {
-                    var ktx = new KhronosTextureContainer(data, 6);
-
-                    var loadMipmap = ktx.numberOfMipmapLevels > 1 && !noMipmap;
+            if (loader) {
+                rootUrl = loader.transformUrl(rootUrl, this._textureFormatInUse);
 
 
+                const onloaddata = (data: any) => {
                     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
-                    this._unpackFlipY(true);
-
-                    ktx.uploadLevels(texture, !noMipmap);
-
-                    this.setCubeMapTextureParams(gl, loadMipmap);
-
-                    texture.width = ktx.pixelWidth;
-                    texture.height = ktx.pixelHeight;
-                    texture.isReady = true;
-                }, undefined, undefined, true, onerror);
-            }
-            else if (isEnv) {
-                this._loadFile(rootUrl, (data) => {
-                    data = data as ArrayBuffer;
-                    var info = EnvironmentTextureTools.GetEnvInfo(data);
-                    if (info) {
-                        texture.width = info.width;
-                        texture.height = info.width;
-
-                        EnvironmentTextureTools.UploadEnvSpherical(texture, info);
-                        EnvironmentTextureTools.UploadEnvLevelsAsync(texture, data, info).then(() => {
-                            texture.isReady = true;
-                            if (onLoad) {
-                                onLoad();
-                            }
-                        });
+                    loader!.loadCubeData(data, texture, createPolynomials, onLoad, onError);
+                };
+                if (files && files.length === 6) {
+                    if (loader.supportCascades) {
+                        this._cascadeLoadFiles(scene, onloaddata, files, onError);
                     }
                     }
                     else if (onError) {
                     else if (onError) {
-                        onError("Can not parse the environment file", null);
+                        onError("Textures type does not support cascades.");
                     }
                     }
-                }, undefined, undefined, true, onerror);
-            }
-            else if (isDDS) {
-                if (files && files.length === 6) {
-                    this._cascadeLoadFiles(
-                        scene,
-                        imgs => {
-                            var info: DDSInfo | undefined;
-                            var loadMipmap: boolean = false;
-                            var width: number = 0;
-                            for (let index = 0; index < imgs.length; index++) {
-                                let data = imgs[index];
-                                info = DDSTools.GetDDSInfo(data);
-
-                                texture.width = info.width;
-                                texture.height = info.height;
-                                width = info.width;
-
-                                loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
-
-                                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
-                                this._unpackFlipY(info.isCompressed);
-
-                                DDSTools.UploadDDSLevels(this, texture, data, info, loadMipmap, 6, -1, index);
-
-                                if (!noMipmap && !info.isFourCC && info.mipmapCount === 1) {
-                                    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                                }
-                            }
-
-                            this.setCubeMapTextureParams(gl, loadMipmap);
-                            texture.isReady = true;
-
-                            if (onLoad) {
-                                onLoad({ isDDS: true, width: width, info, imgs, texture });
-                            }
-                        },
-                        files,
-                        onError);
-                } else {
-                    this._loadFile(rootUrl,
-                        data => {
-                            var info = DDSTools.GetDDSInfo(data);
-
-                            texture.width = info.width;
-                            texture.height = info.height;
-
-                            if (createPolynomials) {
-                                info.sphericalPolynomial = new SphericalPolynomial();
-                            }
-
-                            var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
-
-                            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
-                            this._unpackFlipY(info.isCompressed);
-
-                            DDSTools.UploadDDSLevels(this, texture, data, info, loadMipmap, 6);
-
-                            if (!noMipmap && !info.isFourCC && info.mipmapCount === 1) {
-                                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                            }
-
-                            this.setCubeMapTextureParams(gl, loadMipmap);
-                            texture.isReady = true;
-
-                            if (onLoad) {
-                                onLoad({ isDDS: true, width: info.width, info, data, texture });
-                            }
-                        },
-                        undefined,
-                        undefined,
-                        true,
-                        onerror);
                 }
                 }
-            } else {
+                else {
+                    this._loadFile(rootUrl, onloaddata, undefined, undefined, true, onInternalError);
+                }
+            }
+            else {
                 if (!files) {
                 if (!files) {
                     throw new Error("Cannot load cubemap because files were not defined");
                     throw new Error("Cannot load cubemap because files were not defined");
                 }
                 }
@@ -5671,7 +5564,7 @@
                         gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                         gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                     }
                     }
 
 
-                    this.setCubeMapTextureParams(gl, !noMipmap);
+                    this._setCubeMapTextureParams(!noMipmap);
 
 
                     texture.width = width;
                     texture.width = width;
                     texture.height = height;
                     texture.height = height;
@@ -5694,7 +5587,11 @@
             return texture;
             return texture;
         }
         }
 
 
-        private setCubeMapTextureParams(gl: WebGLRenderingContext, loadMipmap: boolean) {
+        /**
+         * @hidden
+         */
+        public _setCubeMapTextureParams(loadMipmap: boolean): void {
+            var gl = this._gl;
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);

+ 3 - 0
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -263,6 +263,9 @@ module BABYLON {
             });
             });
         }
         }
 
 
+        /**
+         * Updates the bounding box information for the Gizmo
+         */
         public updateBoundingBox(){
         public updateBoundingBox(){
             if(this.attachedMesh){             
             if(this.attachedMesh){             
                 // Rotate based on axis
                 // Rotate based on axis

+ 123 - 0
src/Materials/Textures/Loaders/babylon.ddsTextureLoader.ts

@@ -0,0 +1,123 @@
+module BABYLON {
+    /**
+     * Implementation of the DDS Texture Loader.
+     */
+    class DDSTextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = true;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            return extension.indexOf(".dds") === 0;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            return rootUrl;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            return null;
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(imgs: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            var engine = texture.getEngine();
+            var info: DDSInfo | undefined;
+            var loadMipmap: boolean = false;
+            if (Array.isArray(imgs)) {
+                for (let index = 0; index < imgs.length; index++) {
+                    let data = imgs[index];
+                    info = DDSTools.GetDDSInfo(data);
+
+                    texture.width = info.width;
+                    texture.height = info.height;
+
+                    loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && texture.generateMipMaps;
+
+                    engine._unpackFlipY(info.isCompressed);
+
+                    DDSTools.UploadDDSLevels(engine, texture, data, info, loadMipmap, 6, -1, index);
+
+                    if (!info.isFourCC && info.mipmapCount === 1) {
+                        engine.generateMipMapsForCubemap(texture);
+                    }
+                }
+            }
+            else {
+                var data = imgs;
+                info = DDSTools.GetDDSInfo(data);
+
+                texture.width = info.width;
+                texture.height = info.height;
+
+                if (createPolynomials) {
+                    info.sphericalPolynomial = new SphericalPolynomial();
+                }
+
+                loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && texture.generateMipMaps;
+                engine._unpackFlipY(info.isCompressed);
+
+                DDSTools.UploadDDSLevels(engine, texture, data, info, loadMipmap, 6);
+
+                if (!info.isFourCC && info.mipmapCount === 1) {
+                    engine.generateMipMapsForCubemap(texture);
+                }
+            }
+
+            engine._setCubeMapTextureParams(loadMipmap);
+            texture.isReady = true;
+
+            if (onLoad) {
+                onLoad({ isDDS: true, width: texture.width, info, imgs, texture });
+            }
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            var info = DDSTools.GetDDSInfo(data);
+
+            var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && texture.generateMipMaps && ((info.width >> (info.mipmapCount - 1)) === 1);
+            callback(info.width, info.height, !loadMipmap, info.isFourCC, () => {
+                DDSTools.UploadDDSLevels(texture.getEngine(), texture, data, info, loadMipmap, 1);
+            });
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.push(new DDSTextureLoader());
+}

+ 90 - 0
src/Materials/Textures/Loaders/babylon.envTextureLoader.ts

@@ -0,0 +1,90 @@
+module BABYLON {
+    /**
+     * Implementation of the ENV Texture Loader.
+     */
+    class ENVTextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = false;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            return extension.indexOf(".env") === 0;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            return rootUrl;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            return null;
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            if (Array.isArray(data)) {
+                return;
+            }
+
+            data = data as ArrayBuffer;
+            var info = EnvironmentTextureTools.GetEnvInfo(data);
+            if (info) {
+                texture.width = info.width;
+                texture.height = info.width;
+
+                EnvironmentTextureTools.UploadEnvSpherical(texture, info);
+                EnvironmentTextureTools.UploadEnvLevelsAsync(texture, data, info).then(() => {
+                    texture.isReady = true;
+                    if (onLoad) {
+                        onLoad();
+                    }
+                });
+            }
+            else if (onError) {
+                onError("Can not parse the environment file", null);
+            }
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            throw ".env not supported in 2d.";
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.push(new ENVTextureLoader());
+}

+ 97 - 0
src/Materials/Textures/Loaders/babylon.ktxTextureLoader.ts

@@ -0,0 +1,97 @@
+module BABYLON {
+    /**
+     * Implementation of the KTX Texture Loader.
+     */
+    class KTXTextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = false;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            if (textureFormatInUse && !isBase64 && !fallback && !isBuffer) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            var lastDot = rootUrl.lastIndexOf('.');
+            return (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + textureFormatInUse;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            // remove the format appended to the rootUrl in the original createCubeTexture call.
+            var exp = new RegExp("" + textureFormatInUse! + "$");
+            return rootUrl.replace(exp, "");
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            if (Array.isArray(data)) {
+                return;
+            }
+
+            var engine = texture.getEngine();
+            var ktx = new KhronosTextureContainer(data, 6);
+
+            var loadMipmap = ktx.numberOfMipmapLevels > 1 && texture.generateMipMaps;
+
+            engine._unpackFlipY(true);
+
+            ktx.uploadLevels(texture, texture.generateMipMaps);
+
+            texture.width = ktx.pixelWidth;
+            texture.height = ktx.pixelHeight;
+
+            engine._setCubeMapTextureParams(loadMipmap);
+            texture.isReady = true;
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            var ktx = new KhronosTextureContainer(data, 1);
+
+            callback(ktx.pixelWidth, ktx.pixelHeight, false, true, () => {
+                ktx.uploadLevels(texture, texture.generateMipMaps);
+            });
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.unshift(new KTXTextureLoader());
+}

+ 75 - 0
src/Materials/Textures/Loaders/babylon.tgaTextureLoader.ts

@@ -0,0 +1,75 @@
+module BABYLON {
+    /**
+     * Implementation of the TGA Texture Loader.
+     */
+    class TGATextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = false;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            return extension.indexOf(".tga") === 0;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            return rootUrl;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            return null;
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            throw ".env not supported in Cube.";
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            var uintData = new Uint8Array(data);
+
+            var header = TGATools.GetTGAHeader(uintData);
+            callback(header.width, header.height, texture.generateMipMaps, false, () => {
+                TGATools.UploadContent(texture, uintData);
+            });
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.push(new TGATextureLoader());
+}

+ 57 - 0
src/Materials/Textures/babylon.internalTextureLoader.ts

@@ -0,0 +1,57 @@
+module BABYLON {
+    /**
+     * This represents the required contract to create a new type of texture loader.
+     */
+    export interface IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        supportCascades: boolean;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean;
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string;
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string>;
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+    }
+}