Explorar o código

Merge branch 'master' into joystickCanvasPublic

Trevor Baron %!s(int64=6) %!d(string=hai) anos
pai
achega
3b41c63a5a

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 6588 - 6559
Playground/babylon.d.txt


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3399 - 3374
dist/preview release/babylon.d.ts


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/babylon.js


+ 73 - 13
dist/preview release/babylon.max.js

@@ -2302,6 +2302,9 @@ var BABYLON;
                         }
                     };
                     request.addEventListener("readystatechange", onReadyStateChange);
+                    if (Tools.UseCustomRequestHeaders) {
+                        Tools.InjectCustomRequestHeaders(request);
+                    }
                     request.send();
                 };
                 retryLoop(0);
@@ -3096,6 +3099,18 @@ var BABYLON;
                 console.timeEnd(counterName);
             }
         };
+        /**
+         * Injects the @see CustomRequestHeaders into the given request
+         * @param request the request that should be used for injection
+         */
+        Tools.InjectCustomRequestHeaders = function (request) {
+            for (var key in Tools.CustomRequestHeaders) {
+                var val = Tools.CustomRequestHeaders[key];
+                if (val) {
+                    request.setRequestHeader(key, val);
+                }
+            }
+        };
         Object.defineProperty(Tools, "Now", {
             /**
              * Gets either window.performance.now() if supported or Date.now() else
@@ -3215,6 +3230,18 @@ var BABYLON;
          */
         Tools.BaseUrl = "";
         /**
+         * Enable/Disable Custom HTTP Request Headers globally.
+         * default = false
+         * @see CustomRequestHeaders
+         */
+        Tools.UseCustomRequestHeaders = false;
+        /**
+         * Custom HTTP Request Headers to be sent with XMLHttpRequests
+         * i.e. when loading files, where the server/service expects an Authorization header.
+         * @see InjectCustomRequestHeaders injects them to an XMLHttpRequest
+         */
+        Tools.CustomRequestHeaders = {};
+        /**
          * Gets or sets the retry strategy to apply when an error happens while loading an asset
          */
         Tools.DefaultRetryStrategy = RetryStrategy.ExponentialBackoff();
@@ -15699,9 +15726,10 @@ var BABYLON;
          * @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 forcedExtension defines the extension to use to pick the right loader
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (default: empty array)
          * @returns a InternalTexture for assignment back into BABYLON.Texture
          */
-        Engine.prototype.createTexture = function (urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, fallback, format, forcedExtension) {
+        Engine.prototype.createTexture = function (urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, fallback, format, forcedExtension, excludeLoaders) {
             var _this = this;
             if (samplingMode === void 0) { samplingMode = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE; }
             if (onLoad === void 0) { onLoad = null; }
@@ -15710,6 +15738,7 @@ var BABYLON;
             if (fallback === void 0) { fallback = null; }
             if (format === void 0) { format = null; }
             if (forcedExtension === void 0) { forcedExtension = null; }
+            if (excludeLoaders === void 0) { excludeLoaders = []; }
             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 fromBlob = url.substr(0, 5) === "blob:";
@@ -15721,7 +15750,7 @@ var BABYLON;
             var loader = null;
             for (var _i = 0, _a = Engine._TextureLoaders; _i < _a.length; _i++) {
                 var availableLoader = _a[_i];
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
                     loader = availableLoader;
                     break;
                 }
@@ -15757,7 +15786,9 @@ var BABYLON;
                     if (fallbackUrl) {
                         // Add Back
                         customFallback = true;
-                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        excludeLoaders.push(loader);
+                        BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
+                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                     }
                 }
                 if (!customFallback) {
@@ -15775,11 +15806,16 @@ var BABYLON;
             // processing for non-image formats
             if (loader) {
                 var callback = function (data) {
-                    loader.loadData(data, texture, function (width, height, loadMipmap, isCompressed, done) {
-                        _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
-                            done();
-                            return false;
-                        }, samplingMode);
+                    loader.loadData(data, texture, function (width, height, loadMipmap, isCompressed, done, loadFailed) {
+                        if (loadFailed) {
+                            onInternalError("TextureLoader failed to load data");
+                        }
+                        else {
+                            _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
+                                done();
+                                return false;
+                            }, samplingMode);
+                        }
                     });
                 };
                 if (!buffer) {
@@ -16876,9 +16912,10 @@ var BABYLON;
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
          * @param fallback defines texture to use while falling back when (compressed) texture file not found.
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (defualt: empty array)
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback, excludeLoaders) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -16887,6 +16924,7 @@ var BABYLON;
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
             if (fallback === void 0) { fallback = null; }
+            if (excludeLoaders === void 0) { excludeLoaders = []; }
             var gl = this._gl;
             var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
@@ -16903,16 +16941,18 @@ var BABYLON;
             var loader = null;
             for (var _i = 0, _a = Engine._TextureLoaders; _i < _a.length; _i++) {
                 var availableLoader = _a[_i];
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
                     loader = availableLoader;
                     break;
                 }
             }
             var onInternalError = function (request, exception) {
                 if (loader) {
-                    var fallbackUrl = loader.getFallbackTextureUrl(rootUrl, _this._textureFormatInUse);
+                    var fallbackUrl = loader.getFallbackTextureUrl(texture.url, _this._textureFormatInUse);
+                    BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
                     if (fallbackUrl) {
-                        _this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                        excludeLoaders.push(loader);
+                        _this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture, excludeLoaders);
                     }
                 }
                 if (onError && request) {
@@ -80316,6 +80356,9 @@ var BABYLON;
                     // It could fail when coupled with HTML5 Offline API
                     var retryManifestURL = _this._currentSceneUrl + ".manifest";
                     xhr.open("GET", retryManifestURL, true);
+                    if (BABYLON.Tools.UseCustomRequestHeaders) {
+                        BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -80323,6 +80366,9 @@ var BABYLON;
                 }
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -80529,6 +80575,9 @@ var BABYLON;
                         BABYLON.Tools.Error("Error in XHR request in BABYLON.Database.");
                         image.src = url;
                     }, false);
+                    if (BABYLON.Tools.CustomRequestHeaders) {
+                        BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -80778,6 +80827,9 @@ var BABYLON;
                     BABYLON.Tools.Error("error on XHR request.");
                     callback();
                 }, false);
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             else {
@@ -81979,6 +82031,9 @@ var BABYLON;
                 noConfigFile();
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -101092,12 +101147,17 @@ var BABYLON;
         /** contents of the KTX container file */
         arrayBuffer, facesExpected, threeDExpected, textureArrayExpected) {
             this.arrayBuffer = arrayBuffer;
+            /**
+             * If the container has been made invalid (eg. constructor failed to correctly load array buffer)
+             */
+            this.isInvalid = false;
             // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is:
             // '�', 'K', 'T', 'X', ' ', '1', '1', '�', '\r', '\n', '\x1A', '\n'
             // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
             var identifier = new Uint8Array(this.arrayBuffer, 0, 12);
             if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 || identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 ||
                 identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D || identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
+                this.isInvalid = true;
                 BABYLON.Tools.Error("texture missing KTX identifier");
                 return;
             }
@@ -101284,7 +101344,7 @@ var BABYLON;
             var ktx = new BABYLON.KhronosTextureContainer(data, 1);
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, function () {
                 ktx.uploadLevels(texture, texture.generateMipMaps);
-            });
+            }, ktx.isInvalid);
         };
         return KTXTextureLoader;
     }());

+ 73 - 13
dist/preview release/babylon.no-module.max.js

@@ -2269,6 +2269,9 @@ var BABYLON;
                         }
                     };
                     request.addEventListener("readystatechange", onReadyStateChange);
+                    if (Tools.UseCustomRequestHeaders) {
+                        Tools.InjectCustomRequestHeaders(request);
+                    }
                     request.send();
                 };
                 retryLoop(0);
@@ -3063,6 +3066,18 @@ var BABYLON;
                 console.timeEnd(counterName);
             }
         };
+        /**
+         * Injects the @see CustomRequestHeaders into the given request
+         * @param request the request that should be used for injection
+         */
+        Tools.InjectCustomRequestHeaders = function (request) {
+            for (var key in Tools.CustomRequestHeaders) {
+                var val = Tools.CustomRequestHeaders[key];
+                if (val) {
+                    request.setRequestHeader(key, val);
+                }
+            }
+        };
         Object.defineProperty(Tools, "Now", {
             /**
              * Gets either window.performance.now() if supported or Date.now() else
@@ -3182,6 +3197,18 @@ var BABYLON;
          */
         Tools.BaseUrl = "";
         /**
+         * Enable/Disable Custom HTTP Request Headers globally.
+         * default = false
+         * @see CustomRequestHeaders
+         */
+        Tools.UseCustomRequestHeaders = false;
+        /**
+         * Custom HTTP Request Headers to be sent with XMLHttpRequests
+         * i.e. when loading files, where the server/service expects an Authorization header.
+         * @see InjectCustomRequestHeaders injects them to an XMLHttpRequest
+         */
+        Tools.CustomRequestHeaders = {};
+        /**
          * Gets or sets the retry strategy to apply when an error happens while loading an asset
          */
         Tools.DefaultRetryStrategy = RetryStrategy.ExponentialBackoff();
@@ -15666,9 +15693,10 @@ var BABYLON;
          * @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 forcedExtension defines the extension to use to pick the right loader
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (default: empty array)
          * @returns a InternalTexture for assignment back into BABYLON.Texture
          */
-        Engine.prototype.createTexture = function (urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, fallback, format, forcedExtension) {
+        Engine.prototype.createTexture = function (urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, fallback, format, forcedExtension, excludeLoaders) {
             var _this = this;
             if (samplingMode === void 0) { samplingMode = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE; }
             if (onLoad === void 0) { onLoad = null; }
@@ -15677,6 +15705,7 @@ var BABYLON;
             if (fallback === void 0) { fallback = null; }
             if (format === void 0) { format = null; }
             if (forcedExtension === void 0) { forcedExtension = null; }
+            if (excludeLoaders === void 0) { excludeLoaders = []; }
             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 fromBlob = url.substr(0, 5) === "blob:";
@@ -15688,7 +15717,7 @@ var BABYLON;
             var loader = null;
             for (var _i = 0, _a = Engine._TextureLoaders; _i < _a.length; _i++) {
                 var availableLoader = _a[_i];
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
                     loader = availableLoader;
                     break;
                 }
@@ -15724,7 +15753,9 @@ var BABYLON;
                     if (fallbackUrl) {
                         // Add Back
                         customFallback = true;
-                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        excludeLoaders.push(loader);
+                        BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
+                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                     }
                 }
                 if (!customFallback) {
@@ -15742,11 +15773,16 @@ var BABYLON;
             // processing for non-image formats
             if (loader) {
                 var callback = function (data) {
-                    loader.loadData(data, texture, function (width, height, loadMipmap, isCompressed, done) {
-                        _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
-                            done();
-                            return false;
-                        }, samplingMode);
+                    loader.loadData(data, texture, function (width, height, loadMipmap, isCompressed, done, loadFailed) {
+                        if (loadFailed) {
+                            onInternalError("TextureLoader failed to load data");
+                        }
+                        else {
+                            _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
+                                done();
+                                return false;
+                            }, samplingMode);
+                        }
                     });
                 };
                 if (!buffer) {
@@ -16843,9 +16879,10 @@ var BABYLON;
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
          * @param fallback defines texture to use while falling back when (compressed) texture file not found.
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (defualt: empty array)
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback, excludeLoaders) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -16854,6 +16891,7 @@ var BABYLON;
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
             if (fallback === void 0) { fallback = null; }
+            if (excludeLoaders === void 0) { excludeLoaders = []; }
             var gl = this._gl;
             var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
@@ -16870,16 +16908,18 @@ var BABYLON;
             var loader = null;
             for (var _i = 0, _a = Engine._TextureLoaders; _i < _a.length; _i++) {
                 var availableLoader = _a[_i];
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
                     loader = availableLoader;
                     break;
                 }
             }
             var onInternalError = function (request, exception) {
                 if (loader) {
-                    var fallbackUrl = loader.getFallbackTextureUrl(rootUrl, _this._textureFormatInUse);
+                    var fallbackUrl = loader.getFallbackTextureUrl(texture.url, _this._textureFormatInUse);
+                    BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
                     if (fallbackUrl) {
-                        _this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                        excludeLoaders.push(loader);
+                        _this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture, excludeLoaders);
                     }
                 }
                 if (onError && request) {
@@ -80283,6 +80323,9 @@ var BABYLON;
                     // It could fail when coupled with HTML5 Offline API
                     var retryManifestURL = _this._currentSceneUrl + ".manifest";
                     xhr.open("GET", retryManifestURL, true);
+                    if (BABYLON.Tools.UseCustomRequestHeaders) {
+                        BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -80290,6 +80333,9 @@ var BABYLON;
                 }
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -80496,6 +80542,9 @@ var BABYLON;
                         BABYLON.Tools.Error("Error in XHR request in BABYLON.Database.");
                         image.src = url;
                     }, false);
+                    if (BABYLON.Tools.CustomRequestHeaders) {
+                        BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -80745,6 +80794,9 @@ var BABYLON;
                     BABYLON.Tools.Error("error on XHR request.");
                     callback();
                 }, false);
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             else {
@@ -81946,6 +81998,9 @@ var BABYLON;
                 noConfigFile();
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -101059,12 +101114,17 @@ var BABYLON;
         /** contents of the KTX container file */
         arrayBuffer, facesExpected, threeDExpected, textureArrayExpected) {
             this.arrayBuffer = arrayBuffer;
+            /**
+             * If the container has been made invalid (eg. constructor failed to correctly load array buffer)
+             */
+            this.isInvalid = false;
             // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is:
             // '�', 'K', 'T', 'X', ' ', '1', '1', '�', '\r', '\n', '\x1A', '\n'
             // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
             var identifier = new Uint8Array(this.arrayBuffer, 0, 12);
             if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 || identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 ||
                 identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D || identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
+                this.isInvalid = true;
                 BABYLON.Tools.Error("texture missing KTX identifier");
                 return;
             }
@@ -101251,7 +101311,7 @@ var BABYLON;
             var ktx = new BABYLON.KhronosTextureContainer(data, 1);
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, function () {
                 ktx.uploadLevels(texture, texture.generateMipMaps);
-            });
+            }, ktx.isInvalid);
         };
         return KTXTextureLoader;
     }());

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/babylon.worker.js


+ 73 - 13
dist/preview release/es6.js

@@ -2269,6 +2269,9 @@ var BABYLON;
                         }
                     };
                     request.addEventListener("readystatechange", onReadyStateChange);
+                    if (Tools.UseCustomRequestHeaders) {
+                        Tools.InjectCustomRequestHeaders(request);
+                    }
                     request.send();
                 };
                 retryLoop(0);
@@ -3063,6 +3066,18 @@ var BABYLON;
                 console.timeEnd(counterName);
             }
         };
+        /**
+         * Injects the @see CustomRequestHeaders into the given request
+         * @param request the request that should be used for injection
+         */
+        Tools.InjectCustomRequestHeaders = function (request) {
+            for (var key in Tools.CustomRequestHeaders) {
+                var val = Tools.CustomRequestHeaders[key];
+                if (val) {
+                    request.setRequestHeader(key, val);
+                }
+            }
+        };
         Object.defineProperty(Tools, "Now", {
             /**
              * Gets either window.performance.now() if supported or Date.now() else
@@ -3182,6 +3197,18 @@ var BABYLON;
          */
         Tools.BaseUrl = "";
         /**
+         * Enable/Disable Custom HTTP Request Headers globally.
+         * default = false
+         * @see CustomRequestHeaders
+         */
+        Tools.UseCustomRequestHeaders = false;
+        /**
+         * Custom HTTP Request Headers to be sent with XMLHttpRequests
+         * i.e. when loading files, where the server/service expects an Authorization header.
+         * @see InjectCustomRequestHeaders injects them to an XMLHttpRequest
+         */
+        Tools.CustomRequestHeaders = {};
+        /**
          * Gets or sets the retry strategy to apply when an error happens while loading an asset
          */
         Tools.DefaultRetryStrategy = RetryStrategy.ExponentialBackoff();
@@ -15666,9 +15693,10 @@ var BABYLON;
          * @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 forcedExtension defines the extension to use to pick the right loader
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (default: empty array)
          * @returns a InternalTexture for assignment back into BABYLON.Texture
          */
-        Engine.prototype.createTexture = function (urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, fallback, format, forcedExtension) {
+        Engine.prototype.createTexture = function (urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, fallback, format, forcedExtension, excludeLoaders) {
             var _this = this;
             if (samplingMode === void 0) { samplingMode = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE; }
             if (onLoad === void 0) { onLoad = null; }
@@ -15677,6 +15705,7 @@ var BABYLON;
             if (fallback === void 0) { fallback = null; }
             if (format === void 0) { format = null; }
             if (forcedExtension === void 0) { forcedExtension = null; }
+            if (excludeLoaders === void 0) { excludeLoaders = []; }
             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 fromBlob = url.substr(0, 5) === "blob:";
@@ -15688,7 +15717,7 @@ var BABYLON;
             var loader = null;
             for (var _i = 0, _a = Engine._TextureLoaders; _i < _a.length; _i++) {
                 var availableLoader = _a[_i];
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
                     loader = availableLoader;
                     break;
                 }
@@ -15724,7 +15753,9 @@ var BABYLON;
                     if (fallbackUrl) {
                         // Add Back
                         customFallback = true;
-                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        excludeLoaders.push(loader);
+                        BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
+                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                     }
                 }
                 if (!customFallback) {
@@ -15742,11 +15773,16 @@ var BABYLON;
             // processing for non-image formats
             if (loader) {
                 var callback = function (data) {
-                    loader.loadData(data, texture, function (width, height, loadMipmap, isCompressed, done) {
-                        _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
-                            done();
-                            return false;
-                        }, samplingMode);
+                    loader.loadData(data, texture, function (width, height, loadMipmap, isCompressed, done, loadFailed) {
+                        if (loadFailed) {
+                            onInternalError("TextureLoader failed to load data");
+                        }
+                        else {
+                            _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
+                                done();
+                                return false;
+                            }, samplingMode);
+                        }
                     });
                 };
                 if (!buffer) {
@@ -16843,9 +16879,10 @@ var BABYLON;
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
          * @param fallback defines texture to use while falling back when (compressed) texture file not found.
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (defualt: empty array)
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback, excludeLoaders) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -16854,6 +16891,7 @@ var BABYLON;
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
             if (fallback === void 0) { fallback = null; }
+            if (excludeLoaders === void 0) { excludeLoaders = []; }
             var gl = this._gl;
             var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
@@ -16870,16 +16908,18 @@ var BABYLON;
             var loader = null;
             for (var _i = 0, _a = Engine._TextureLoaders; _i < _a.length; _i++) {
                 var availableLoader = _a[_i];
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
                     loader = availableLoader;
                     break;
                 }
             }
             var onInternalError = function (request, exception) {
                 if (loader) {
-                    var fallbackUrl = loader.getFallbackTextureUrl(rootUrl, _this._textureFormatInUse);
+                    var fallbackUrl = loader.getFallbackTextureUrl(texture.url, _this._textureFormatInUse);
+                    BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
                     if (fallbackUrl) {
-                        _this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                        excludeLoaders.push(loader);
+                        _this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture, excludeLoaders);
                     }
                 }
                 if (onError && request) {
@@ -80283,6 +80323,9 @@ var BABYLON;
                     // It could fail when coupled with HTML5 Offline API
                     var retryManifestURL = _this._currentSceneUrl + ".manifest";
                     xhr.open("GET", retryManifestURL, true);
+                    if (BABYLON.Tools.UseCustomRequestHeaders) {
+                        BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -80290,6 +80333,9 @@ var BABYLON;
                 }
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -80496,6 +80542,9 @@ var BABYLON;
                         BABYLON.Tools.Error("Error in XHR request in BABYLON.Database.");
                         image.src = url;
                     }, false);
+                    if (BABYLON.Tools.CustomRequestHeaders) {
+                        BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -80745,6 +80794,9 @@ var BABYLON;
                     BABYLON.Tools.Error("error on XHR request.");
                     callback();
                 }, false);
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             else {
@@ -81946,6 +81998,9 @@ var BABYLON;
                 noConfigFile();
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -101059,12 +101114,17 @@ var BABYLON;
         /** contents of the KTX container file */
         arrayBuffer, facesExpected, threeDExpected, textureArrayExpected) {
             this.arrayBuffer = arrayBuffer;
+            /**
+             * If the container has been made invalid (eg. constructor failed to correctly load array buffer)
+             */
+            this.isInvalid = false;
             // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is:
             // '�', 'K', 'T', 'X', ' ', '1', '1', '�', '\r', '\n', '\x1A', '\n'
             // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
             var identifier = new Uint8Array(this.arrayBuffer, 0, 12);
             if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 || identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 ||
                 identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D || identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
+                this.isInvalid = true;
                 BABYLON.Tools.Error("texture missing KTX identifier");
                 return;
             }
@@ -101251,7 +101311,7 @@ var BABYLON;
             var ktx = new BABYLON.KhronosTextureContainer(data, 1);
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, function () {
                 ktx.uploadLevels(texture, texture.generateMipMaps);
-            });
+            }, ktx.isInvalid);
         };
         return KTXTextureLoader;
     }());

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/gui/babylon.gui.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 2 - 0
dist/preview release/what's new.md

@@ -60,8 +60,10 @@
 - Added opacity texture support to `GridMaterial` ([Deltakosh](https://github.com/deltakosh))
 - Added support for deserializing morph target animations in animation groups
 - AssetContainer dispose method ([TrevorDev](https://github.com/TrevorDev))
+- Loading texture with KTX will fallback to non-KTX loader if KTX loader fails ([TrevorDev](https://github.com/TrevorDev))
 - `Layer` are now supported in `RenderTargetTexture` ([Sebavan](https://github.com/Sebavan))
 - Make onscreen joystick's canvas public ([TrevorDev](https://github.com/TrevorDev))
+- Added `Tools.CustomRequestHeaders`, `Tools.UseCustomRequestHeaders`, `Tools.InjectCustomRequestHeaders` to send Custom Request Headers alongside XMLHttpRequest's i.e. when loading files (Tools.Loadfile) from resources requiring special headers like 'Authorization' ([susares](https://github.com/susares))
 
 ### glTF Loader
 

+ 1 - 0
gui/src/3D/gui3DManager.ts

@@ -53,6 +53,7 @@ export class GUI3DManager implements IDisposable {
 
         this._utilityLayer = new UtilityLayerRenderer(this._scene);
         this._utilityLayer.onlyCheckPointerDownEvents = false;
+        this._utilityLayer.pickUtilitySceneFirst = false;
         this._utilityLayer.mainSceneTrackerPredicate = (mesh: Nullable<AbstractMesh>) => {
             return mesh && mesh.metadata && mesh.metadata._node;
         };

+ 22 - 13
src/Engine/babylon.engine.ts

@@ -4243,12 +4243,13 @@ module BABYLON {
          * @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 forcedExtension defines the extension to use to pick the right loader
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (default: empty array)
          * @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,
             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,
-            forcedExtension: Nullable<string> = null): InternalTexture {
+            forcedExtension: Nullable<string> = null, excludeLoaders: Array<IInternalTextureLoader> = []): InternalTexture {
             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 fromBlob = url.substr(0, 5) === "blob:";
@@ -4262,7 +4263,7 @@ module BABYLON {
 
             let loader: Nullable<IInternalTextureLoader> = null;
             for (let availableLoader of Engine._TextureLoaders) {
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
                     loader = availableLoader;
                     break;
                 }
@@ -4303,7 +4304,9 @@ module BABYLON {
                     if (fallbackUrl) {
                         // Add Back
                         customFallback = true;
-                        this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        excludeLoaders.push(loader);
+                        Tools.Warn((loader.constructor as any).name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
+                        this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                     }
                 }
 
@@ -4324,12 +4327,15 @@ module BABYLON {
             // processing for non-image formats
             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);
+                    loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed) => {
+                        if (loadFailed) {
+                            onInternalError("TextureLoader failed to load data");
+                        }else {
+                            this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
+                                done();
+                                return false;
+                            }, samplingMode);
+                        }
                     });
                 };
 
@@ -5674,9 +5680,10 @@ module BABYLON {
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
          * @param fallback defines texture to use while falling back when (compressed) texture file not found.
+         * @param excludeLoaders array of texture loaders that should be excluded when picking a loader for the texture (defualt: empty array)
          * @returns the cube texture as an InternalTexture
          */
-        public createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials = false, lodScale: number = 0, lodOffset: number = 0, fallback: Nullable<InternalTexture> = null): InternalTexture {
+        public createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials = false, lodScale: number = 0, lodOffset: number = 0, fallback: Nullable<InternalTexture> = null, excludeLoaders: Array<IInternalTextureLoader> = []): InternalTexture {
             var gl = this._gl;
 
             var texture = fallback ? fallback : new InternalTexture(this, InternalTexture.DATASOURCE_CUBE);
@@ -5696,7 +5703,7 @@ module BABYLON {
 
             let loader: Nullable<IInternalTextureLoader> = null;
             for (let availableLoader of Engine._TextureLoaders) {
-                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
+                if (excludeLoaders.indexOf(availableLoader) === -1 && availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
                     loader = availableLoader;
                     break;
                 }
@@ -5704,9 +5711,11 @@ module BABYLON {
 
             let onInternalError = (request?: XMLHttpRequest, exception?: any) => {
                 if (loader) {
-                    const fallbackUrl = loader.getFallbackTextureUrl(rootUrl, this._textureFormatInUse);
+                    const fallbackUrl = loader.getFallbackTextureUrl(texture.url, this._textureFormatInUse);
+                    Tools.Warn((loader.constructor as any).name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
                     if (fallbackUrl) {
-                        this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                        excludeLoaders.push(loader);
+                        this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture, excludeLoaders);
                     }
                 }
 

+ 2 - 2
src/Materials/Textures/Loaders/babylon.ktxTextureLoader.ts

@@ -83,12 +83,12 @@ module BABYLON {
          * @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 {
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void {
             var ktx = new KhronosTextureContainer(data, 1);
 
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, () => {
                 ktx.uploadLevels(texture, texture.generateMipMaps);
-            });
+            }, ktx.isInvalid);
         }
     }
 

+ 4 - 1
src/Materials/Textures/Procedurals/babylon.customProceduralTexture.ts

@@ -72,6 +72,9 @@ module BABYLON {
             }, false);
 
             try {
+                if (Tools.UseCustomRequestHeaders) {
+                    Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -164,4 +167,4 @@ module BABYLON {
             this._animate = value;
         }
     }
-}
+}

+ 1 - 1
src/Materials/Textures/babylon.internalTextureLoader.ts

@@ -52,6 +52,6 @@ module BABYLON {
          * @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;
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed?: boolean) => void): void;
     }
 }

+ 15 - 0
src/Offline/babylon.database.ts

@@ -142,6 +142,9 @@ module BABYLON {
                     // It could fail when coupled with HTML5 Offline API
                     var retryManifestURL = this._currentSceneUrl + ".manifest";
                     xhr.open("GET", retryManifestURL, true);
+                    if (Tools.UseCustomRequestHeaders) {
+                        Tools.InjectCustomRequestHeaders(xhr);
+                    }
                     xhr.send();
                 }
                 else {
@@ -150,6 +153,10 @@ module BABYLON {
             }, false);
 
             try {
+                if (Tools.UseCustomRequestHeaders) {
+                    Tools.InjectCustomRequestHeaders(xhr);
+                }
+
                 xhr.send();
             }
             catch (ex) {
@@ -377,6 +384,10 @@ module BABYLON {
                         image.src = url;
                     }, false);
 
+                    if (Tools.CustomRequestHeaders) {
+                        Tools.InjectCustomRequestHeaders(xhr);
+                    }
+
                     xhr.send();
                 }
                 else {
@@ -652,6 +663,10 @@ module BABYLON {
                     callback();
                 }, false);
 
+                if (Tools.UseCustomRequestHeaders) {
+                    Tools.InjectCustomRequestHeaders(xhr);
+                }
+
                 xhr.send();
             }
             else {

+ 6 - 1
src/Tools/babylon.khronosTextureContainer.ts

@@ -65,6 +65,10 @@ module BABYLON {
          * Gets the load type
          */
         public loadType: number;
+        /**
+         * If the container has been made invalid (eg. constructor failed to correctly load array buffer)
+         */
+        public isInvalid = false;
 
         /**
          * Creates a new KhronosTextureContainer
@@ -82,7 +86,8 @@ module BABYLON {
             var identifier = new Uint8Array(this.arrayBuffer, 0, 12);
             if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 || identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 ||
                 identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D || identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
-                Tools.Error("texture missing KTX identifier");
+                this.isInvalid = true;
+                    Tools.Error("texture missing KTX identifier");
                 return;
             }
 

+ 31 - 0
src/Tools/babylon.tools.ts

@@ -203,6 +203,20 @@ module BABYLON {
         public static BaseUrl = "";
 
         /**
+         * Enable/Disable Custom HTTP Request Headers globally.
+         * default = false
+         * @see CustomRequestHeaders
+         */
+        public static UseCustomRequestHeaders: boolean = false;
+
+        /**
+         * Custom HTTP Request Headers to be sent with XMLHttpRequests
+         * i.e. when loading files, where the server/service expects an Authorization header.
+         * @see InjectCustomRequestHeaders injects them to an XMLHttpRequest
+         */
+        public static CustomRequestHeaders: { [key: string]: string } = {};
+
+        /**
          * Gets or sets the retry strategy to apply when an error happens while loading an asset
          */
         public static DefaultRetryStrategy = RetryStrategy.ExponentialBackoff();
@@ -943,6 +957,10 @@ module BABYLON {
 
                     request.addEventListener("readystatechange", onReadyStateChange);
 
+                    if (Tools.UseCustomRequestHeaders) {
+                       Tools.InjectCustomRequestHeaders(request);
+                    }
+
                     request.send();
                 };
 
@@ -1882,6 +1900,19 @@ module BABYLON {
         }
 
         /**
+         * Injects the @see CustomRequestHeaders into the given request
+         * @param request the request that should be used for injection
+         */
+        public static InjectCustomRequestHeaders(request: XMLHttpRequest): void {
+            for (let key in Tools.CustomRequestHeaders) {
+                const val = Tools.CustomRequestHeaders[key];
+                if (val) {
+                    request.setRequestHeader(key, val);
+                }
+            }
+        }
+
+        /**
          * Starts a performance counter
          */
         public static StartPerformanceCounter: (counterName: string, condition?: boolean) => void = Tools._StartPerformanceCounterDisabled;