sebastien 6 سال پیش
والد
کامیت
1c87eabb43
35فایلهای تغییر یافته به همراه11816 افزوده شده و 8712 حذف شده
  1. 7526 4782
      Playground/babylon.d.txt
  2. 3812 3780
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 106 30
      dist/preview release/babylon.max.js
  5. 106 30
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 106 30
      dist/preview release/es6.js
  8. 1 1
      dist/preview release/glTF2Interface/package.json
  9. 4 0
      dist/preview release/gui/babylon.gui.d.ts
  10. 1 1
      dist/preview release/gui/babylon.gui.js
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js
  12. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  13. 8 0
      dist/preview release/gui/babylon.gui.module.d.ts
  14. 2 2
      dist/preview release/gui/package.json
  15. 5 5
      dist/preview release/inspector/package.json
  16. 3 3
      dist/preview release/loaders/package.json
  17. 2 2
      dist/preview release/materialsLibrary/package.json
  18. 2 2
      dist/preview release/postProcessesLibrary/package.json
  19. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  20. 3 3
      dist/preview release/serializers/package.json
  21. 1 1
      dist/preview release/viewer/babylon.viewer.js
  22. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  23. 3 0
      dist/preview release/what's new.md
  24. 5 0
      gui/src/2D/controls/control.ts
  25. 3 1
      gui/src/2D/valueAndUnit.ts
  26. 1 0
      gui/src/3D/gui3DManager.ts
  27. 1 1
      package.json
  28. 27 14
      src/Engine/engine.ts
  29. 2 2
      src/Materials/Textures/Loaders/ktxTextureLoader.ts
  30. 3 0
      src/Materials/Textures/Procedurals/customProceduralTexture.ts
  31. 1 1
      src/Materials/Textures/internalTextureLoader.ts
  32. 23 13
      src/Materials/Textures/videoTexture.ts
  33. 15 0
      src/Offline/database.ts
  34. 6 1
      src/Tools/khronosTextureContainer.ts
  35. 31 0
      src/Tools/tools.ts

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 7526 - 4782
Playground/babylon.d.txt


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3812 - 3780
dist/preview release/babylon.d.ts


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/babylon.js


+ 106 - 30
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();
@@ -12918,7 +12945,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.6";
+                return "4.0.0-alpha.7";
             },
             enumerable: true,
             configurable: true
@@ -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) {
@@ -28766,6 +28806,14 @@ var BABYLON;
             }
             return null;
         };
+        Scene.prototype._getGeometryByUniqueID = function (id) {
+            for (var index = 0; index < this.geometries.length; index++) {
+                if (this.geometries[index].uniqueId === id) {
+                    return this.geometries[index];
+                }
+            }
+            return null;
+        };
         /**
          * Add a new geometry to this scene
          * @param geometry defines the geometry to be added to the scene.
@@ -28773,7 +28821,7 @@ var BABYLON;
          * @return a boolean defining if the geometry was added or not
          */
         Scene.prototype.pushGeometry = function (geometry, force) {
-            if (!force && this.getGeometryByID(geometry.id)) {
+            if (!force && this._getGeometryByUniqueID(geometry.uniqueId)) {
                 return false;
             }
             this.addGeometry(geometry);
@@ -31349,9 +31397,9 @@ var BABYLON;
                     return;
                 case InternalTexture.DATASOURCE_URL:
                     proxy = this._engine.createTexture(this.url, !this.generateMipMaps, this.invertY, null, this.samplingMode, function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     }, null, this._buffer, undefined, this.format);
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_RAW:
                     proxy = this._engine.createRawTexture(this._bufferView, this.baseWidth, this.baseHeight, this.format, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
@@ -31402,9 +31450,9 @@ var BABYLON;
                     return;
                 case InternalTexture.DATASOURCE_CUBE:
                     proxy = this._engine.createCubeTexture(this.url, null, this._files, !this.generateMipMaps, function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     }, null, this.format, this._extension);
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_CUBERAW:
                     proxy = this._engine.createRawCubeTexture(this._bufferViewArray, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
@@ -31414,9 +31462,9 @@ var BABYLON;
                 case InternalTexture.DATASOURCE_CUBERAW_RGBD:
                     proxy = this._engine.createRawCubeTexture(null, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
                     BABYLON.RawCubeTexture._UpdateRGBDAsync(proxy, this._bufferViewArrayArray, this._sphericalPolynomial, this._lodGenerationScale, this._lodGenerationOffset).then(function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     });
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_CUBEPREFILTERED:
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, function (proxy) {
@@ -40984,6 +41032,7 @@ var BABYLON;
             this._isDisposed = false;
             this._indexBufferIsUpdatable = false;
             this.id = id;
+            this.uniqueId = scene.getUniqueId();
             this._engine = scene.getEngine();
             this._meshes = [];
             this._scene = scene;
@@ -75424,12 +75473,12 @@ var BABYLON;
             var _this = _super.call(this, null, scene, !generateMipMaps, invertY) || this;
             _this._onUserActionRequestedObservable = null;
             _this._stillImageCaptured = false;
-            _this._poster = false;
+            _this._displayingPosterTexture = false;
             _this._createInternalTexture = function () {
                 if (_this._texture != null) {
-                    if (_this._poster) {
+                    if (_this._displayingPosterTexture) {
                         _this._texture.dispose();
-                        _this._poster = false;
+                        _this._displayingPosterTexture = false;
                     }
                     else {
                         return;
@@ -75446,7 +75495,7 @@ var BABYLON;
                     _this._generateMipMaps = false;
                 }
                 _this._texture = _this._engine.createDynamicTexture(_this.video.videoWidth, _this.video.videoHeight, _this._generateMipMaps, _this._samplingMode);
-                if (!_this.video.autoplay) {
+                if (!_this.video.autoplay && !_this._settings.poster) {
                     var oldHandler_1 = _this.video.onplaying;
                     var error_1 = false;
                     _this.video.onplaying = function () {
@@ -75494,7 +75543,7 @@ var BABYLON;
                 if (_this._texture == null) {
                     return;
                 }
-                if (!_this._poster) {
+                if (!_this._displayingPosterTexture) {
                     _this._texture.dispose();
                     _this._texture = null;
                 }
@@ -75506,6 +75555,9 @@ var BABYLON;
                 if (_this.video.readyState < _this.video.HAVE_CURRENT_DATA) {
                     return;
                 }
+                if (_this._displayingPosterTexture) {
+                    return;
+                }
                 _this._engine.updateVideoTexture(_this._texture, _this.video, _this._invertY);
             };
             _this._engine = _this.getScene().getEngine();
@@ -75514,6 +75566,7 @@ var BABYLON;
             _this.autoUpdateTexture = settings.autoUpdateTexture;
             _this.name = name || _this._getName(src);
             _this.video = _this._getVideo(src);
+            _this._settings = settings;
             if (settings.poster) {
                 _this.video.poster = settings.poster;
             }
@@ -75524,16 +75577,19 @@ var BABYLON;
                 _this.video.loop = settings.loop;
             }
             _this.video.setAttribute("playsinline", "");
-            _this.video.addEventListener("canplay", _this._createInternalTexture);
             _this.video.addEventListener("paused", _this._updateInternalTexture);
             _this.video.addEventListener("seeked", _this._updateInternalTexture);
             _this.video.addEventListener("emptied", _this.reset);
-            if (_this.video.readyState >= _this.video.HAVE_CURRENT_DATA) {
-                _this._createInternalTexture();
-            }
-            if (settings.poster) {
+            _this._createInternalTextureOnEvent = (settings.poster && !settings.autoPlay) ? "play" : "canplay";
+            _this.video.addEventListener(_this._createInternalTextureOnEvent, _this._createInternalTexture);
+            var videoHasEnoughData = (_this.video.readyState >= _this.video.HAVE_CURRENT_DATA);
+            if (settings.poster &&
+                (!settings.autoPlay || !videoHasEnoughData)) {
                 _this._texture = _this._engine.createTexture(settings.poster, false, true, scene);
-                _this._poster = true;
+                _this._displayingPosterTexture = true;
+            }
+            else if (videoHasEnoughData) {
+                _this._createInternalTexture();
             }
             return _this;
         }
@@ -75626,7 +75682,7 @@ var BABYLON;
                 this._onUserActionRequestedObservable.clear();
                 this._onUserActionRequestedObservable = null;
             }
-            this.video.removeEventListener("canplay", this._createInternalTexture);
+            this.video.removeEventListener(this._createInternalTextureOnEvent, this._createInternalTexture);
             this.video.removeEventListener("paused", this._updateInternalTexture);
             this.video.removeEventListener("seeked", this._updateInternalTexture);
             this.video.removeEventListener("emptied", this.reset);
@@ -80300,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 {
@@ -80307,6 +80366,9 @@ var BABYLON;
                 }
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -80513,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 {
@@ -80762,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 {
@@ -81963,6 +82031,9 @@ var BABYLON;
                 noConfigFile();
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -101076,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;
             }
@@ -101268,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;
     }());

+ 106 - 30
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();
@@ -12885,7 +12912,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.6";
+                return "4.0.0-alpha.7";
             },
             enumerable: true,
             configurable: true
@@ -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) {
@@ -28733,6 +28773,14 @@ var BABYLON;
             }
             return null;
         };
+        Scene.prototype._getGeometryByUniqueID = function (id) {
+            for (var index = 0; index < this.geometries.length; index++) {
+                if (this.geometries[index].uniqueId === id) {
+                    return this.geometries[index];
+                }
+            }
+            return null;
+        };
         /**
          * Add a new geometry to this scene
          * @param geometry defines the geometry to be added to the scene.
@@ -28740,7 +28788,7 @@ var BABYLON;
          * @return a boolean defining if the geometry was added or not
          */
         Scene.prototype.pushGeometry = function (geometry, force) {
-            if (!force && this.getGeometryByID(geometry.id)) {
+            if (!force && this._getGeometryByUniqueID(geometry.uniqueId)) {
                 return false;
             }
             this.addGeometry(geometry);
@@ -31316,9 +31364,9 @@ var BABYLON;
                     return;
                 case InternalTexture.DATASOURCE_URL:
                     proxy = this._engine.createTexture(this.url, !this.generateMipMaps, this.invertY, null, this.samplingMode, function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     }, null, this._buffer, undefined, this.format);
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_RAW:
                     proxy = this._engine.createRawTexture(this._bufferView, this.baseWidth, this.baseHeight, this.format, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
@@ -31369,9 +31417,9 @@ var BABYLON;
                     return;
                 case InternalTexture.DATASOURCE_CUBE:
                     proxy = this._engine.createCubeTexture(this.url, null, this._files, !this.generateMipMaps, function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     }, null, this.format, this._extension);
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_CUBERAW:
                     proxy = this._engine.createRawCubeTexture(this._bufferViewArray, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
@@ -31381,9 +31429,9 @@ var BABYLON;
                 case InternalTexture.DATASOURCE_CUBERAW_RGBD:
                     proxy = this._engine.createRawCubeTexture(null, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
                     BABYLON.RawCubeTexture._UpdateRGBDAsync(proxy, this._bufferViewArrayArray, this._sphericalPolynomial, this._lodGenerationScale, this._lodGenerationOffset).then(function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     });
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_CUBEPREFILTERED:
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, function (proxy) {
@@ -40951,6 +40999,7 @@ var BABYLON;
             this._isDisposed = false;
             this._indexBufferIsUpdatable = false;
             this.id = id;
+            this.uniqueId = scene.getUniqueId();
             this._engine = scene.getEngine();
             this._meshes = [];
             this._scene = scene;
@@ -75391,12 +75440,12 @@ var BABYLON;
             var _this = _super.call(this, null, scene, !generateMipMaps, invertY) || this;
             _this._onUserActionRequestedObservable = null;
             _this._stillImageCaptured = false;
-            _this._poster = false;
+            _this._displayingPosterTexture = false;
             _this._createInternalTexture = function () {
                 if (_this._texture != null) {
-                    if (_this._poster) {
+                    if (_this._displayingPosterTexture) {
                         _this._texture.dispose();
-                        _this._poster = false;
+                        _this._displayingPosterTexture = false;
                     }
                     else {
                         return;
@@ -75413,7 +75462,7 @@ var BABYLON;
                     _this._generateMipMaps = false;
                 }
                 _this._texture = _this._engine.createDynamicTexture(_this.video.videoWidth, _this.video.videoHeight, _this._generateMipMaps, _this._samplingMode);
-                if (!_this.video.autoplay) {
+                if (!_this.video.autoplay && !_this._settings.poster) {
                     var oldHandler_1 = _this.video.onplaying;
                     var error_1 = false;
                     _this.video.onplaying = function () {
@@ -75461,7 +75510,7 @@ var BABYLON;
                 if (_this._texture == null) {
                     return;
                 }
-                if (!_this._poster) {
+                if (!_this._displayingPosterTexture) {
                     _this._texture.dispose();
                     _this._texture = null;
                 }
@@ -75473,6 +75522,9 @@ var BABYLON;
                 if (_this.video.readyState < _this.video.HAVE_CURRENT_DATA) {
                     return;
                 }
+                if (_this._displayingPosterTexture) {
+                    return;
+                }
                 _this._engine.updateVideoTexture(_this._texture, _this.video, _this._invertY);
             };
             _this._engine = _this.getScene().getEngine();
@@ -75481,6 +75533,7 @@ var BABYLON;
             _this.autoUpdateTexture = settings.autoUpdateTexture;
             _this.name = name || _this._getName(src);
             _this.video = _this._getVideo(src);
+            _this._settings = settings;
             if (settings.poster) {
                 _this.video.poster = settings.poster;
             }
@@ -75491,16 +75544,19 @@ var BABYLON;
                 _this.video.loop = settings.loop;
             }
             _this.video.setAttribute("playsinline", "");
-            _this.video.addEventListener("canplay", _this._createInternalTexture);
             _this.video.addEventListener("paused", _this._updateInternalTexture);
             _this.video.addEventListener("seeked", _this._updateInternalTexture);
             _this.video.addEventListener("emptied", _this.reset);
-            if (_this.video.readyState >= _this.video.HAVE_CURRENT_DATA) {
-                _this._createInternalTexture();
-            }
-            if (settings.poster) {
+            _this._createInternalTextureOnEvent = (settings.poster && !settings.autoPlay) ? "play" : "canplay";
+            _this.video.addEventListener(_this._createInternalTextureOnEvent, _this._createInternalTexture);
+            var videoHasEnoughData = (_this.video.readyState >= _this.video.HAVE_CURRENT_DATA);
+            if (settings.poster &&
+                (!settings.autoPlay || !videoHasEnoughData)) {
                 _this._texture = _this._engine.createTexture(settings.poster, false, true, scene);
-                _this._poster = true;
+                _this._displayingPosterTexture = true;
+            }
+            else if (videoHasEnoughData) {
+                _this._createInternalTexture();
             }
             return _this;
         }
@@ -75593,7 +75649,7 @@ var BABYLON;
                 this._onUserActionRequestedObservable.clear();
                 this._onUserActionRequestedObservable = null;
             }
-            this.video.removeEventListener("canplay", this._createInternalTexture);
+            this.video.removeEventListener(this._createInternalTextureOnEvent, this._createInternalTexture);
             this.video.removeEventListener("paused", this._updateInternalTexture);
             this.video.removeEventListener("seeked", this._updateInternalTexture);
             this.video.removeEventListener("emptied", this.reset);
@@ -80267,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 {
@@ -80274,6 +80333,9 @@ var BABYLON;
                 }
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -80480,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 {
@@ -80729,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 {
@@ -81930,6 +81998,9 @@ var BABYLON;
                 noConfigFile();
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -101043,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;
             }
@@ -101235,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;
     }());

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/babylon.worker.js


+ 106 - 30
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();
@@ -12885,7 +12912,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.6";
+                return "4.0.0-alpha.7";
             },
             enumerable: true,
             configurable: true
@@ -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) {
@@ -28733,6 +28773,14 @@ var BABYLON;
             }
             return null;
         };
+        Scene.prototype._getGeometryByUniqueID = function (id) {
+            for (var index = 0; index < this.geometries.length; index++) {
+                if (this.geometries[index].uniqueId === id) {
+                    return this.geometries[index];
+                }
+            }
+            return null;
+        };
         /**
          * Add a new geometry to this scene
          * @param geometry defines the geometry to be added to the scene.
@@ -28740,7 +28788,7 @@ var BABYLON;
          * @return a boolean defining if the geometry was added or not
          */
         Scene.prototype.pushGeometry = function (geometry, force) {
-            if (!force && this.getGeometryByID(geometry.id)) {
+            if (!force && this._getGeometryByUniqueID(geometry.uniqueId)) {
                 return false;
             }
             this.addGeometry(geometry);
@@ -31316,9 +31364,9 @@ var BABYLON;
                     return;
                 case InternalTexture.DATASOURCE_URL:
                     proxy = this._engine.createTexture(this.url, !this.generateMipMaps, this.invertY, null, this.samplingMode, function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     }, null, this._buffer, undefined, this.format);
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_RAW:
                     proxy = this._engine.createRawTexture(this._bufferView, this.baseWidth, this.baseHeight, this.format, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
@@ -31369,9 +31417,9 @@ var BABYLON;
                     return;
                 case InternalTexture.DATASOURCE_CUBE:
                     proxy = this._engine.createCubeTexture(this.url, null, this._files, !this.generateMipMaps, function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     }, null, this.format, this._extension);
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_CUBERAW:
                     proxy = this._engine.createRawCubeTexture(this._bufferViewArray, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
@@ -31381,9 +31429,9 @@ var BABYLON;
                 case InternalTexture.DATASOURCE_CUBERAW_RGBD:
                     proxy = this._engine.createRawCubeTexture(null, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
                     BABYLON.RawCubeTexture._UpdateRGBDAsync(proxy, this._bufferViewArrayArray, this._sphericalPolynomial, this._lodGenerationScale, this._lodGenerationOffset).then(function () {
+                        proxy._swapAndDie(_this);
                         _this.isReady = true;
                     });
-                    proxy._swapAndDie(this);
                     return;
                 case InternalTexture.DATASOURCE_CUBEPREFILTERED:
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, function (proxy) {
@@ -40951,6 +40999,7 @@ var BABYLON;
             this._isDisposed = false;
             this._indexBufferIsUpdatable = false;
             this.id = id;
+            this.uniqueId = scene.getUniqueId();
             this._engine = scene.getEngine();
             this._meshes = [];
             this._scene = scene;
@@ -75391,12 +75440,12 @@ var BABYLON;
             var _this = _super.call(this, null, scene, !generateMipMaps, invertY) || this;
             _this._onUserActionRequestedObservable = null;
             _this._stillImageCaptured = false;
-            _this._poster = false;
+            _this._displayingPosterTexture = false;
             _this._createInternalTexture = function () {
                 if (_this._texture != null) {
-                    if (_this._poster) {
+                    if (_this._displayingPosterTexture) {
                         _this._texture.dispose();
-                        _this._poster = false;
+                        _this._displayingPosterTexture = false;
                     }
                     else {
                         return;
@@ -75413,7 +75462,7 @@ var BABYLON;
                     _this._generateMipMaps = false;
                 }
                 _this._texture = _this._engine.createDynamicTexture(_this.video.videoWidth, _this.video.videoHeight, _this._generateMipMaps, _this._samplingMode);
-                if (!_this.video.autoplay) {
+                if (!_this.video.autoplay && !_this._settings.poster) {
                     var oldHandler_1 = _this.video.onplaying;
                     var error_1 = false;
                     _this.video.onplaying = function () {
@@ -75461,7 +75510,7 @@ var BABYLON;
                 if (_this._texture == null) {
                     return;
                 }
-                if (!_this._poster) {
+                if (!_this._displayingPosterTexture) {
                     _this._texture.dispose();
                     _this._texture = null;
                 }
@@ -75473,6 +75522,9 @@ var BABYLON;
                 if (_this.video.readyState < _this.video.HAVE_CURRENT_DATA) {
                     return;
                 }
+                if (_this._displayingPosterTexture) {
+                    return;
+                }
                 _this._engine.updateVideoTexture(_this._texture, _this.video, _this._invertY);
             };
             _this._engine = _this.getScene().getEngine();
@@ -75481,6 +75533,7 @@ var BABYLON;
             _this.autoUpdateTexture = settings.autoUpdateTexture;
             _this.name = name || _this._getName(src);
             _this.video = _this._getVideo(src);
+            _this._settings = settings;
             if (settings.poster) {
                 _this.video.poster = settings.poster;
             }
@@ -75491,16 +75544,19 @@ var BABYLON;
                 _this.video.loop = settings.loop;
             }
             _this.video.setAttribute("playsinline", "");
-            _this.video.addEventListener("canplay", _this._createInternalTexture);
             _this.video.addEventListener("paused", _this._updateInternalTexture);
             _this.video.addEventListener("seeked", _this._updateInternalTexture);
             _this.video.addEventListener("emptied", _this.reset);
-            if (_this.video.readyState >= _this.video.HAVE_CURRENT_DATA) {
-                _this._createInternalTexture();
-            }
-            if (settings.poster) {
+            _this._createInternalTextureOnEvent = (settings.poster && !settings.autoPlay) ? "play" : "canplay";
+            _this.video.addEventListener(_this._createInternalTextureOnEvent, _this._createInternalTexture);
+            var videoHasEnoughData = (_this.video.readyState >= _this.video.HAVE_CURRENT_DATA);
+            if (settings.poster &&
+                (!settings.autoPlay || !videoHasEnoughData)) {
                 _this._texture = _this._engine.createTexture(settings.poster, false, true, scene);
-                _this._poster = true;
+                _this._displayingPosterTexture = true;
+            }
+            else if (videoHasEnoughData) {
+                _this._createInternalTexture();
             }
             return _this;
         }
@@ -75593,7 +75649,7 @@ var BABYLON;
                 this._onUserActionRequestedObservable.clear();
                 this._onUserActionRequestedObservable = null;
             }
-            this.video.removeEventListener("canplay", this._createInternalTexture);
+            this.video.removeEventListener(this._createInternalTextureOnEvent, this._createInternalTexture);
             this.video.removeEventListener("paused", this._updateInternalTexture);
             this.video.removeEventListener("seeked", this._updateInternalTexture);
             this.video.removeEventListener("emptied", this.reset);
@@ -80267,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 {
@@ -80274,6 +80333,9 @@ var BABYLON;
                 }
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -80480,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 {
@@ -80729,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 {
@@ -81930,6 +81998,9 @@ var BABYLON;
                 noConfigFile();
             }, false);
             try {
+                if (BABYLON.Tools.UseCustomRequestHeaders) {
+                    BABYLON.Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {
@@ -101043,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;
             }
@@ -101235,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;
     }());

+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 4 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -879,6 +879,10 @@ declare module BABYLON.GUI {
             protected _disabledColor: string;
             /** @hidden */
             _tag: any;
+            /**
+                * Gets or sets an object used to store user defined information for the node
+                */
+            metadata: any;
             /** Gets or sets a boolean indicating if the control can be hit with pointer events */
             isHitTestVisible: boolean;
             /** Gets or sets a boolean indicating if the control can block pointer events */

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/gui/babylon.gui.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


+ 8 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -986,6 +986,10 @@ declare module 'babylonjs-gui/2D/controls/control' {
             protected _disabledColor: string;
             /** @hidden */
             _tag: any;
+            /**
+                * Gets or sets an object used to store user defined information for the node
+                */
+            metadata: any;
             /** Gets or sets a boolean indicating if the control can be hit with pointer events */
             isHitTestVisible: boolean;
             /** Gets or sets a boolean indicating if the control can block pointer events */
@@ -3866,6 +3870,10 @@ declare module BABYLON.GUI {
             protected _disabledColor: string;
             /** @hidden */
             _tag: any;
+            /**
+                * Gets or sets an object used to store user defined information for the node
+                */
+            metadata: any;
             /** Gets or sets a boolean indicating if the control can be hit with pointer events */
             isHitTestVisible: boolean;
             /** Gets or sets a boolean indicating if the control can block pointer events */

+ 2 - 2
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.6"
+        "babylonjs": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

+ 5 - 5
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.6",
-        "babylonjs-gui": "4.0.0-alpha.6",
-        "babylonjs-loaders": "4.0.0-alpha.6",
-        "babylonjs-serializers": "4.0.0-alpha.6"
+        "babylonjs": "4.0.0-alpha.7",
+        "babylonjs-gui": "4.0.0-alpha.7",
+        "babylonjs-loaders": "4.0.0-alpha.7",
+        "babylonjs-serializers": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

+ 3 - 3
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.6",
-        "babylonjs": "4.0.0-alpha.6"
+        "babylonjs-gltf2interface": "4.0.0-alpha.7",
+        "babylonjs": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.6"
+        "babylonjs": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.6"
+        "babylonjs": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.6"
+        "babylonjs": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

+ 3 - 3
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.6",
-        "babylonjs-gltf2interface": "4.0.0-alpha.6"
+        "babylonjs": "4.0.0-alpha.7",
+        "babylonjs-gltf2interface": "4.0.0-alpha.7"
     },
     "engines": {
         "node": "*"

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -60,7 +60,9 @@
 - 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))
+- 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
 
@@ -96,6 +98,7 @@
 - Add missing effect layer to asset container ([TrevorDev](https://github.com/TrevorDev))
 - Fixed effect layer compatibility with multi materials ([Sebavan](https://github.com/Sebavan))
 - Added a `DeepImmutable<T>` type to specifiy that a referenced object should be considered recursively immutable, meaning that all its properties are `readonly` and that if a property is a reference to an object, this object is also recursively immutable. ([barroij](https://github.com/barroij))
+- Fixed `VideoTexture` poster property when autoplay is turned off.
 
 ### Viewer
 

+ 5 - 0
gui/src/2D/controls/control.ts

@@ -86,6 +86,11 @@ export class Control {
     /** @hidden */
     public _tag: any;
 
+    /**
+     * Gets or sets an object used to store user defined information for the node
+     */
+    public metadata: any = null;
+
     /** Gets or sets a boolean indicating if the control can be hit with pointer events */
     public isHitTestVisible = true;
     /** Gets or sets a boolean indicating if the control can block pointer events */

+ 3 - 1
gui/src/2D/valueAndUnit.ts

@@ -5,6 +5,7 @@ import { AdvancedDynamicTexture } from "./advancedDynamicTexture";
  */
 export class ValueAndUnit {
     private _value = 1;
+    private _originalUnit: number;
     /**
      * Gets or sets a value indicating that this value will not scale accordingly with adaptive scaling property
      * @see http://doc.babylonjs.com/how_to/gui#adaptive-scaling
@@ -23,6 +24,7 @@ export class ValueAndUnit {
         /** defines a boolean indicating if the value can be negative */
         public negativeValueAllowed = true) {
         this._value = value;
+        this._originalUnit = unit;
     }
 
     /** Gets a boolean indicating if the value is a percentage */
@@ -116,7 +118,7 @@ export class ValueAndUnit {
         }
 
         var sourceValue = parseFloat(match[1]);
-        var sourceUnit = this.unit;
+        var sourceUnit = this._originalUnit;
 
         if (!this.negativeValueAllowed) {
             if (sourceValue < 0) {

+ 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;
         };

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.0.0-alpha.6",
+    "version": "4.0.0-alpha.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 27 - 14
src/Engine/engine.ts

@@ -514,7 +514,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "4.0.0-alpha.6";
+            return "4.0.0-alpha.7";
         }
 
         /**
@@ -4269,12 +4269,17 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
          * @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
+<<<<<<< HEAD:src/Engine/engine.ts
          * @returns a InternalTexture for assignment back into Texture
+=======
+         * @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
+>>>>>>> upstream/master:src/Engine/babylon.engine.ts
          */
         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:";
@@ -4288,7 +4293,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
 
             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;
                 }
@@ -4329,7 +4334,9 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
                     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);
                     }
                 }
 
@@ -4350,12 +4357,15 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
             // 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);
+                        }
                     });
                 };
 
@@ -5700,9 +5710,10 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
          * @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);
@@ -5722,7 +5733,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
 
             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;
                 }
@@ -5730,9 +5741,11 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
 
             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/ktxTextureLoader.ts

@@ -89,12 +89,12 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "States";
          * @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);
         }
     }
 

+ 3 - 0
src/Materials/Textures/Procedurals/customProceduralTexture.ts

@@ -78,6 +78,9 @@ import { ProceduralTexture } from "./proceduralTexture";
             }, false);
 
             try {
+                if (Tools.UseCustomRequestHeaders) {
+                    Tools.InjectCustomRequestHeaders(xhr);
+                }
                 xhr.send();
             }
             catch (ex) {

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

@@ -55,5 +55,5 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "States";
          * @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;
     }

+ 23 - 13
src/Materials/Textures/videoTexture.ts

@@ -61,7 +61,9 @@ import { Texture } from "Materials/Textures/texture";
         private _generateMipMaps: boolean;
         private _engine: Engine;
         private _stillImageCaptured = false;
-        private _poster = false;
+        private _displayingPosterTexture = false;
+        private _settings: VideoTextureSettings;
+        private _createInternalTextureOnEvent: string;
 
         /**
          * Creates a video texture.
@@ -98,6 +100,8 @@ import { Texture } from "Materials/Textures/texture";
 
             this.name = name || this._getName(src);
             this.video = this._getVideo(src);
+            this._settings = settings;
+
             if (settings.poster) {
                 this.video.poster = settings.poster;
             }
@@ -111,18 +115,20 @@ import { Texture } from "Materials/Textures/texture";
 
             this.video.setAttribute("playsinline", "");
 
-            this.video.addEventListener("canplay", this._createInternalTexture);
             this.video.addEventListener("paused", this._updateInternalTexture);
             this.video.addEventListener("seeked", this._updateInternalTexture);
             this.video.addEventListener("emptied", this.reset);
+            this._createInternalTextureOnEvent = (settings.poster && !settings.autoPlay) ? "play" : "canplay";
+            this.video.addEventListener(this._createInternalTextureOnEvent, this._createInternalTexture);
 
-            if (this.video.readyState >= this.video.HAVE_CURRENT_DATA) {
-                this._createInternalTexture();
-            }
-
-            if (settings.poster) {
+            const videoHasEnoughData = (this.video.readyState >= this.video.HAVE_CURRENT_DATA);
+            if (settings.poster &&
+                (!settings.autoPlay || !videoHasEnoughData)) {
                 this._texture = this._engine.createTexture(settings.poster!, false, true, scene);
-                this._poster = true;
+                this._displayingPosterTexture = true;
+            }
+            else if (videoHasEnoughData) {
+                this._createInternalTexture();
             }
         }
 
@@ -160,9 +166,9 @@ import { Texture } from "Materials/Textures/texture";
 
         private _createInternalTexture = (): void => {
             if (this._texture != null) {
-                if (this._poster) {
+                if (this._displayingPosterTexture) {
                     this._texture.dispose();
-                    this._poster = false;
+                    this._displayingPosterTexture = false;
                 }
                 else {
                     return;
@@ -186,7 +192,7 @@ import { Texture } from "Materials/Textures/texture";
                 this._samplingMode
             );
 
-            if (!this.video.autoplay) {
+            if (!this.video.autoplay && !this._settings.poster) {
                 let oldHandler = this.video.onplaying;
                 let error = false;
                 this.video.onplaying = () => {
@@ -236,7 +242,7 @@ import { Texture } from "Materials/Textures/texture";
                 return;
             }
 
-            if (!this._poster) {
+            if (!this._displayingPosterTexture) {
                 this._texture.dispose();
                 this._texture = null;
             }
@@ -283,6 +289,10 @@ import { Texture } from "Materials/Textures/texture";
             if (this.video.readyState < this.video.HAVE_CURRENT_DATA) {
                 return;
             }
+            if (this._displayingPosterTexture) {
+                return;
+            }
+
             this._engine.updateVideoTexture(this._texture, this.video, this._invertY);
         }
 
@@ -305,7 +315,7 @@ import { Texture } from "Materials/Textures/texture";
                 this._onUserActionRequestedObservable = null;
             }
 
-            this.video.removeEventListener("canplay", this._createInternalTexture);
+            this.video.removeEventListener(this._createInternalTextureOnEvent, this._createInternalTexture);
             this.video.removeEventListener("paused", this._updateInternalTexture);
             this.video.removeEventListener("seeked", this._updateInternalTexture);
             this.video.removeEventListener("emptied", this.reset);

+ 15 - 0
src/Offline/database.ts

@@ -144,6 +144,9 @@ import { IOfflineProvider } from "./IOfflineProvider";
                     // 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 {
@@ -152,6 +155,10 @@ import { IOfflineProvider } from "./IOfflineProvider";
             }, false);
 
             try {
+                if (Tools.UseCustomRequestHeaders) {
+                    Tools.InjectCustomRequestHeaders(xhr);
+                }
+
                 xhr.send();
             }
             catch (ex) {
@@ -378,6 +385,10 @@ import { IOfflineProvider } from "./IOfflineProvider";
                         image.src = url;
                     }, false);
 
+                    if (Tools.CustomRequestHeaders) {
+                        Tools.InjectCustomRequestHeaders(xhr);
+                    }
+
                     xhr.send();
                 }
                 else {
@@ -653,6 +664,10 @@ import { IOfflineProvider } from "./IOfflineProvider";
                     callback();
                 }, false);
 
+                if (Tools.UseCustomRequestHeaders) {
+                    Tools.InjectCustomRequestHeaders(xhr);
+                }
+
                 xhr.send();
             }
             else {

+ 6 - 1
src/Tools/khronosTextureContainer.ts

@@ -67,6 +67,10 @@ import { InternalTexture } from "Materials/Textures/internalTexture";
          * 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
@@ -84,7 +88,8 @@ import { InternalTexture } from "Materials/Textures/internalTexture";
             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/tools.ts

@@ -218,6 +218,20 @@ declare type Engine = import("Engine/engine").Engine;
         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();
@@ -944,6 +958,10 @@ declare type Engine = import("Engine/engine").Engine;
 
                     request.addEventListener("readystatechange", onReadyStateChange);
 
+                    if (Tools.UseCustomRequestHeaders) {
+                       Tools.InjectCustomRequestHeaders(request);
+                    }
+
                     request.send();
                 };
 
@@ -1883,6 +1901,19 @@ declare type Engine = import("Engine/engine").Engine;
         }
 
         /**
+         * 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;