ソースを参照

Merge remote-tracking branch 'upstream/master' into InProgress

Sebastien Vandenberghe 7 年 前
コミット
1edd9cf5c1
37 ファイル変更38735 行追加38045 行削除
  1. 15389 15369
      Playground/babylon.d.txt
  2. 2 0
      Playground/scenes/TwoQuads/TwoQuads.gltf
  3. 1 1
      Tools/Gulp/gulp-addModuleExports.js
  4. 12424 12404
      dist/preview release/babylon.d.ts
  5. 49 49
      dist/preview release/babylon.js
  6. 55 3
      dist/preview release/babylon.max.js
  7. 49 49
      dist/preview release/babylon.worker.js
  8. 9752 9732
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  9. 35 35
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  10. 131 45
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  11. 130 44
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  12. 54 2
      dist/preview release/es6.js
  13. 15 2
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  14. 27 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  15. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  16. 21 8
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  17. 74 41
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  19. 22 8
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  20. 75 41
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  22. 75 41
      dist/preview release/loaders/babylonjs.loaders.js
  23. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  24. 22 8
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  25. 44 44
      dist/preview release/viewer/babylon.viewer.js
  26. 56 4
      dist/preview release/viewer/babylon.viewer.max.js
  27. 2 1
      dist/preview release/what's new.md
  28. 1 0
      loaders/src/glTF/1.0/babylon.glTFLoader.ts
  29. 16 19
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  30. 6 2
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  31. 30 23
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  32. 3 3
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  33. 28 2
      loaders/src/glTF/babylon.glTFFileLoader.ts
  34. 27 1
      src/Animations/babylon.animationGroup.ts
  35. 13 2
      src/Helpers/babylon.environmentHelper.ts
  36. 23 0
      src/Tools/babylon.promise.ts
  37. 74 52
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

ファイルの差分が大きいため隠しています
+ 15389 - 15369
Playground/babylon.d.txt


+ 2 - 0
Playground/scenes/TwoQuads/TwoQuads.gltf

@@ -121,6 +121,7 @@
   ],
   "nodes": [
     {
+      "name": "node0",
       "mesh": 0,
       "translation": [
         -0.55,
@@ -129,6 +130,7 @@
       ]
     },
     {
+      "name": "node1",
       "mesh": 0,
       "translation": [
         0.55,

+ 1 - 1
Tools/Gulp/gulp-addModuleExports.js

@@ -25,7 +25,7 @@ module.exports = function (varName, subModule, extendsRoot, externalUsingBabylon
     if(typeof exports === 'object' && typeof module === 'object')
         module.exports = factory(${subModule || extendsRoot ? 'require("babylonjs")' : ''});
     else if(typeof define === 'function' && define.amd)
-        define("${varName.module}", ${subModule || extendsRoot ? '["babylonjs"],' : ''} factory);
+        define("${varName.module}", ${subModule || extendsRoot ? '["babylonjs"],' : '[],'} factory);
     else if(typeof exports === 'object')
         exports["${varName.module}"] = factory(${subModule || extendsRoot ? 'require("babylonjs")' : ''});
     else {

ファイルの差分が大きいため隠しています
+ 12424 - 12404
dist/preview release/babylon.d.ts


ファイルの差分が大きいため隠しています
+ 49 - 49
dist/preview release/babylon.js


+ 55 - 3
dist/preview release/babylon.max.js

@@ -20,7 +20,7 @@ var __extends = (this && this.__extends) || (function () {
     if(typeof exports === 'object' && typeof module === 'object')
         module.exports = factory();
     else if(typeof define === 'function' && define.amd)
-        define("babylonjs",  factory);
+        define("babylonjs", [], factory);
     else if(typeof exports === 'object')
         exports["babylonjs"] = factory();
     else {
@@ -9512,6 +9512,27 @@ var BABYLON;
             }
             return newPromise;
         };
+        InternalPromise.race = function (promises) {
+            var newPromise = new InternalPromise();
+            if (promises.length) {
+                for (var _i = 0, promises_1 = promises; _i < promises_1.length; _i++) {
+                    var promise = promises_1[_i];
+                    promise.then(function (value) {
+                        if (newPromise) {
+                            newPromise._resolve(value);
+                            newPromise = null;
+                        }
+                        return null;
+                    }, function (reason) {
+                        if (newPromise) {
+                            newPromise._reject(reason);
+                            newPromise = null;
+                        }
+                    });
+                }
+            }
+            return newPromise;
+        };
         return InternalPromise;
     }());
     /**
@@ -47099,6 +47120,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "animatables", {
+            /**
+             * returning the list of animatables controlled by this animation group.
+             */
+            get: function () {
+                return this._animatables;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Add an animation (with its target) in the group
          * @param animation defines the animation we want to add
@@ -47259,6 +47290,22 @@ var BABYLON;
             return this;
         };
         /**
+         * Goes to a specific frame in this animation group
+         *
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        AnimationGroup.prototype.goToFrame = function (frame) {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+            return this;
+        };
+        /**
          * Dispose all associated resources
          */
         AnimationGroup.prototype.dispose = function () {
@@ -90919,8 +90966,13 @@ var BABYLON;
          * @param scene The scene to add the material to
          */
         function EnvironmentHelper(options, scene) {
+            var _this = this;
+            this._errorHandler = function (message, exception) {
+                _this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+            };
             this._options = __assign({}, EnvironmentHelper._getDefaultOptions(), options);
             this._scene = scene;
+            this.onErrorObservable = new BABYLON.Observable();
             this._setupBackground();
             this._setupImageProcessing();
         }
@@ -91247,7 +91299,7 @@ var BABYLON;
                 this._groundMaterial.diffuseTexture = this._options.groundTexture;
                 return;
             }
-            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene);
+            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -91333,7 +91385,7 @@ var BABYLON;
                 this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
                 return;
             }
-            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;

ファイルの差分が大きいため隠しています
+ 49 - 49
dist/preview release/babylon.worker.js


ファイルの差分が大きいため隠しています
+ 9752 - 9732
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


ファイルの差分が大きいため隠しています
+ 35 - 35
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 131 - 45
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -20,7 +20,7 @@ var __extends = (this && this.__extends) || (function () {
     if(typeof exports === 'object' && typeof module === 'object')
         module.exports = factory();
     else if(typeof define === 'function' && define.amd)
-        define("babylonjs",  factory);
+        define("babylonjs", [], factory);
     else if(typeof exports === 'object')
         exports["babylonjs"] = factory();
     else {
@@ -9512,6 +9512,27 @@ var BABYLON;
             }
             return newPromise;
         };
+        InternalPromise.race = function (promises) {
+            var newPromise = new InternalPromise();
+            if (promises.length) {
+                for (var _i = 0, promises_1 = promises; _i < promises_1.length; _i++) {
+                    var promise = promises_1[_i];
+                    promise.then(function (value) {
+                        if (newPromise) {
+                            newPromise._resolve(value);
+                            newPromise = null;
+                        }
+                        return null;
+                    }, function (reason) {
+                        if (newPromise) {
+                            newPromise._reject(reason);
+                            newPromise = null;
+                        }
+                    });
+                }
+            }
+            return newPromise;
+        };
         return InternalPromise;
     }());
     /**
@@ -47099,6 +47120,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "animatables", {
+            /**
+             * returning the list of animatables controlled by this animation group.
+             */
+            get: function () {
+                return this._animatables;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Add an animation (with its target) in the group
          * @param animation defines the animation we want to add
@@ -47259,6 +47290,22 @@ var BABYLON;
             return this;
         };
         /**
+         * Goes to a specific frame in this animation group
+         *
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        AnimationGroup.prototype.goToFrame = function (frame) {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+            return this;
+        };
+        /**
          * Dispose all associated resources
          */
         AnimationGroup.prototype.dispose = function () {
@@ -89828,8 +89875,13 @@ var BABYLON;
          * @param scene The scene to add the material to
          */
         function EnvironmentHelper(options, scene) {
+            var _this = this;
+            this._errorHandler = function (message, exception) {
+                _this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+            };
             this._options = __assign({}, EnvironmentHelper._getDefaultOptions(), options);
             this._scene = scene;
+            this.onErrorObservable = new BABYLON.Observable();
             this._setupBackground();
             this._setupImageProcessing();
         }
@@ -90156,7 +90208,7 @@ var BABYLON;
                 this._groundMaterial.diffuseTexture = this._options.groundTexture;
                 return;
             }
-            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene);
+            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -90242,7 +90294,7 @@ var BABYLON;
                 this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
                 return;
             }
-            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
@@ -90562,6 +90614,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -90624,6 +90680,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -90654,6 +90720,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -92322,6 +92400,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
@@ -93142,6 +93221,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -93229,9 +93309,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -93246,6 +93329,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -93324,6 +93408,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -93487,7 +93572,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -93500,12 +93584,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -93800,13 +93884,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -94124,25 +94209,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -94511,7 +94595,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -94548,8 +94632,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -94628,7 +94712,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -94645,7 +94729,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -94814,26 +94901,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
-                        }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
+                        }
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -94846,18 +94932,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

+ 130 - 44
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js

@@ -9485,6 +9485,27 @@ var BABYLON;
             }
             return newPromise;
         };
+        InternalPromise.race = function (promises) {
+            var newPromise = new InternalPromise();
+            if (promises.length) {
+                for (var _i = 0, promises_1 = promises; _i < promises_1.length; _i++) {
+                    var promise = promises_1[_i];
+                    promise.then(function (value) {
+                        if (newPromise) {
+                            newPromise._resolve(value);
+                            newPromise = null;
+                        }
+                        return null;
+                    }, function (reason) {
+                        if (newPromise) {
+                            newPromise._reject(reason);
+                            newPromise = null;
+                        }
+                    });
+                }
+            }
+            return newPromise;
+        };
         return InternalPromise;
     }());
     /**
@@ -47072,6 +47093,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "animatables", {
+            /**
+             * returning the list of animatables controlled by this animation group.
+             */
+            get: function () {
+                return this._animatables;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Add an animation (with its target) in the group
          * @param animation defines the animation we want to add
@@ -47232,6 +47263,22 @@ var BABYLON;
             return this;
         };
         /**
+         * Goes to a specific frame in this animation group
+         *
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        AnimationGroup.prototype.goToFrame = function (frame) {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+            return this;
+        };
+        /**
          * Dispose all associated resources
          */
         AnimationGroup.prototype.dispose = function () {
@@ -89801,8 +89848,13 @@ var BABYLON;
          * @param scene The scene to add the material to
          */
         function EnvironmentHelper(options, scene) {
+            var _this = this;
+            this._errorHandler = function (message, exception) {
+                _this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+            };
             this._options = __assign({}, EnvironmentHelper._getDefaultOptions(), options);
             this._scene = scene;
+            this.onErrorObservable = new BABYLON.Observable();
             this._setupBackground();
             this._setupImageProcessing();
         }
@@ -90129,7 +90181,7 @@ var BABYLON;
                 this._groundMaterial.diffuseTexture = this._options.groundTexture;
                 return;
             }
-            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene);
+            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -90215,7 +90267,7 @@ var BABYLON;
                 this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
                 return;
             }
-            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
@@ -90535,6 +90587,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -90597,6 +90653,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -90627,6 +90693,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -92295,6 +92373,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
@@ -93115,6 +93194,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -93202,9 +93282,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -93219,6 +93302,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -93297,6 +93381,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -93460,7 +93545,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -93473,12 +93557,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -93773,13 +93857,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -94097,25 +94182,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -94484,7 +94568,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -94521,8 +94605,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -94601,7 +94685,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -94618,7 +94702,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -94787,26 +94874,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
-                        }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
+                        }
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -94819,18 +94905,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

+ 54 - 2
dist/preview release/es6.js

@@ -9485,6 +9485,27 @@ var BABYLON;
             }
             return newPromise;
         };
+        InternalPromise.race = function (promises) {
+            var newPromise = new InternalPromise();
+            if (promises.length) {
+                for (var _i = 0, promises_1 = promises; _i < promises_1.length; _i++) {
+                    var promise = promises_1[_i];
+                    promise.then(function (value) {
+                        if (newPromise) {
+                            newPromise._resolve(value);
+                            newPromise = null;
+                        }
+                        return null;
+                    }, function (reason) {
+                        if (newPromise) {
+                            newPromise._reject(reason);
+                            newPromise = null;
+                        }
+                    });
+                }
+            }
+            return newPromise;
+        };
         return InternalPromise;
     }());
     /**
@@ -47072,6 +47093,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "animatables", {
+            /**
+             * returning the list of animatables controlled by this animation group.
+             */
+            get: function () {
+                return this._animatables;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Add an animation (with its target) in the group
          * @param animation defines the animation we want to add
@@ -47232,6 +47263,22 @@ var BABYLON;
             return this;
         };
         /**
+         * Goes to a specific frame in this animation group
+         *
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        AnimationGroup.prototype.goToFrame = function (frame) {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+            return this;
+        };
+        /**
          * Dispose all associated resources
          */
         AnimationGroup.prototype.dispose = function () {
@@ -90892,8 +90939,13 @@ var BABYLON;
          * @param scene The scene to add the material to
          */
         function EnvironmentHelper(options, scene) {
+            var _this = this;
+            this._errorHandler = function (message, exception) {
+                _this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+            };
             this._options = __assign({}, EnvironmentHelper._getDefaultOptions(), options);
             this._scene = scene;
+            this.onErrorObservable = new BABYLON.Observable();
             this._setupBackground();
             this._setupImageProcessing();
         }
@@ -91220,7 +91272,7 @@ var BABYLON;
                 this._groundMaterial.diffuseTexture = this._options.groundTexture;
                 return;
             }
-            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene);
+            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -91306,7 +91358,7 @@ var BABYLON;
                 this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
                 return;
             }
-            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;

+ 15 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -52,6 +52,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -107,13 +108,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -136,6 +143,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -569,6 +581,7 @@ declare module BABYLON.GLTF1 {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;

+ 27 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -77,6 +77,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -139,6 +143,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -169,6 +183,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -1837,6 +1863,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 21 - 8
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -52,6 +52,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -107,13 +108,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -136,6 +143,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -290,6 +302,7 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
@@ -337,7 +350,7 @@ declare module BABYLON.GLTF2 {
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessPropertiesAsync(context, material);
-        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void>;
+        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void>;
         _createMaterial(material: ILoaderMaterial): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void;
@@ -373,7 +386,7 @@ declare module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
@@ -385,7 +398,7 @@ declare module BABYLON.GLTF2 {
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
         static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
         static _LoadUriAsync(loader: GLTFLoader, context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
     }
@@ -404,7 +417,7 @@ declare module BABYLON.GLTF2.Extensions {
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
@@ -428,8 +441,8 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
-        private _loadSpecularGlossinessPropertiesAsync(loader, context, material, properties);
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
+        private _loadSpecularGlossinessPropertiesAsync(context, material, properties);
     }
 }
 

+ 74 - 41
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -77,6 +77,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -139,6 +143,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -169,6 +183,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -499,6 +525,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -586,9 +613,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -603,6 +633,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -681,6 +712,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -844,7 +876,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -857,12 +888,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -1157,13 +1188,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -1481,25 +1513,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -1868,7 +1899,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -1905,8 +1936,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -1994,7 +2025,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -2011,7 +2042,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -2198,26 +2232,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
                         }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -2230,18 +2263,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 22 - 8
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -52,6 +52,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -107,13 +108,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -136,6 +143,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -569,6 +581,7 @@ declare module BABYLON.GLTF1 {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
@@ -845,6 +858,7 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
@@ -892,7 +906,7 @@ declare module BABYLON.GLTF2 {
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessPropertiesAsync(context, material);
-        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void>;
+        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void>;
         _createMaterial(material: ILoaderMaterial): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void;
@@ -928,7 +942,7 @@ declare module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
@@ -940,7 +954,7 @@ declare module BABYLON.GLTF2 {
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
         static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
         static _LoadUriAsync(loader: GLTFLoader, context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
     }
@@ -959,7 +973,7 @@ declare module BABYLON.GLTF2.Extensions {
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
@@ -983,8 +997,8 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
-        private _loadSpecularGlossinessPropertiesAsync(loader, context, material, properties);
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
+        private _loadSpecularGlossinessPropertiesAsync(context, material, properties);
     }
 }
 

+ 75 - 41
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -77,6 +77,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -139,6 +143,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -169,6 +183,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -1837,6 +1863,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
@@ -2675,6 +2702,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -2762,9 +2790,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -2779,6 +2810,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -2857,6 +2889,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -3020,7 +3053,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -3033,12 +3065,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -3333,13 +3365,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -3657,25 +3690,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -4044,7 +4076,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -4081,8 +4113,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -4170,7 +4202,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -4187,7 +4219,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -4374,26 +4409,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
                         }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -4406,18 +4440,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

ファイルの差分が大きいため隠しています
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 75 - 41
dist/preview release/loaders/babylonjs.loaders.js

@@ -1073,6 +1073,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -1135,6 +1139,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -1165,6 +1179,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -2833,6 +2859,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
@@ -3653,6 +3680,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -3740,9 +3768,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -3757,6 +3788,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -3835,6 +3867,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -3998,7 +4031,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -4011,12 +4043,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -4311,13 +4343,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -4635,25 +4668,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -5022,7 +5054,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -5059,8 +5091,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -5139,7 +5171,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -5156,7 +5188,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -5325,26 +5360,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
                         }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -5357,18 +5391,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

ファイルの差分が大きいため隠しています
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 22 - 8
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -154,6 +154,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -209,13 +210,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -238,6 +245,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -671,6 +683,7 @@ declare module BABYLON.GLTF1 {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
@@ -947,6 +960,7 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
@@ -994,7 +1008,7 @@ declare module BABYLON.GLTF2 {
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessPropertiesAsync(context, material);
-        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void>;
+        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void>;
         _createMaterial(material: ILoaderMaterial): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void;
@@ -1030,7 +1044,7 @@ declare module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
@@ -1042,7 +1056,7 @@ declare module BABYLON.GLTF2 {
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
         static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
         static _LoadUriAsync(loader: GLTFLoader, context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
     }
@@ -1061,7 +1075,7 @@ declare module BABYLON.GLTF2.Extensions {
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
@@ -1085,8 +1099,8 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
-        private _loadSpecularGlossinessPropertiesAsync(loader, context, material, properties);
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
+        private _loadSpecularGlossinessPropertiesAsync(context, material, properties);
     }
 }
 

ファイルの差分が大きいため隠しています
+ 44 - 44
dist/preview release/viewer/babylon.viewer.js


+ 56 - 4
dist/preview release/viewer/babylon.viewer.max.js

@@ -4,7 +4,7 @@
     if(typeof exports === 'object' && typeof module === 'object')
         module.exports = factory();
     else if(typeof define === 'function' && define.amd)
-        define("babylonjs-viewer",  factory);
+        define("babylonjs-viewer", [], factory);
     else if(typeof exports === 'object')
         exports["babylonjs-viewer"] = factory();
     else {
@@ -103,7 +103,7 @@ var __extends = (this && this.__extends) || (function () {
     if(true)
         module.exports = factory();
     else if(typeof define === 'function' && define.amd)
-        define("babylonjs",  factory);
+        define("babylonjs", [], factory);
     else if(typeof exports === 'object')
         exports["babylonjs"] = factory();
     else {
@@ -9595,6 +9595,27 @@ var BABYLON;
             }
             return newPromise;
         };
+        InternalPromise.race = function (promises) {
+            var newPromise = new InternalPromise();
+            if (promises.length) {
+                for (var _i = 0, promises_1 = promises; _i < promises_1.length; _i++) {
+                    var promise = promises_1[_i];
+                    promise.then(function (value) {
+                        if (newPromise) {
+                            newPromise._resolve(value);
+                            newPromise = null;
+                        }
+                        return null;
+                    }, function (reason) {
+                        if (newPromise) {
+                            newPromise._reject(reason);
+                            newPromise = null;
+                        }
+                    });
+                }
+            }
+            return newPromise;
+        };
         return InternalPromise;
     }());
     /**
@@ -47182,6 +47203,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "animatables", {
+            /**
+             * returning the list of animatables controlled by this animation group.
+             */
+            get: function () {
+                return this._animatables;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Add an animation (with its target) in the group
          * @param animation defines the animation we want to add
@@ -47342,6 +47373,22 @@ var BABYLON;
             return this;
         };
         /**
+         * Goes to a specific frame in this animation group
+         *
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        AnimationGroup.prototype.goToFrame = function (frame) {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+            return this;
+        };
+        /**
          * Dispose all associated resources
          */
         AnimationGroup.prototype.dispose = function () {
@@ -91002,8 +91049,13 @@ var BABYLON;
          * @param scene The scene to add the material to
          */
         function EnvironmentHelper(options, scene) {
+            var _this = this;
+            this._errorHandler = function (message, exception) {
+                _this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+            };
             this._options = __assign({}, EnvironmentHelper._getDefaultOptions(), options);
             this._scene = scene;
+            this.onErrorObservable = new BABYLON.Observable();
             this._setupBackground();
             this._setupImageProcessing();
         }
@@ -91330,7 +91382,7 @@ var BABYLON;
                 this._groundMaterial.diffuseTexture = this._options.groundTexture;
                 return;
             }
-            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene);
+            var diffuseTexture = new BABYLON.Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -91416,7 +91468,7 @@ var BABYLON;
                 this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
                 return;
             }
-            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new BABYLON.CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;

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

@@ -84,6 +84,7 @@
 - WebVRExperienceHelper will create an empty controller model so that controller interactions can be used while the actual model is still loading ([trevordev](https://github.com/trevordev))
 - Default fragment shader will clamp negative values to avoid underflow, webVR post processing will render to eye texture size ([trevordev](https://github.com/trevordev))
 - Supports Environment Drag and Drop in Sandbox ([sebavan](https://github.com/sebavan))
+- EnvironmentHelper has no an onError observable to handle errors when loading the textures ([RaananW](https://github.com/RaananW))
 
 ## Bug fixes
 
@@ -91,7 +92,7 @@
 - SPS solid particle `.pivot` property now also behaves like the standard mesh pivot. Former behavior (particle translation) can be kept with the particle property `.translateFromPivot` set to true ([jbousquie](https://github.com/jbousquie))
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))
 - SPS internal temporary vector3 instead of Tmp.Vector3 to avoid possible concurrent uses ([jbousquie](https://github.com/jbousquie))
-- Fixed a bug when calling load on an empty assets manager - [#3739](https://github.com/BabylonJS/Babylon.js/issues/3739). ([RaananW](https://github.com/RaananW))
+- Fixed a bug when calling load on an empty assets manager - [#3739](https://github.com/BabylonJS/Babylon.js/issues/3739) ([RaananW](https://github.com/RaananW))
 - Enabling teleportation in the vr helper class caused a redundant post process to be added ([trevordev](https://github.com/trevordev))
 - (Viewer) Fixed a bug where loading another mesh positioned it incorrectly ([RaananW](https://github.com/RaananW))
 - Scale vr controllers by deviceScale when it is set in VRExperienceHelper ([trevordev](https://github.com/trevordev))

+ 1 - 0
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -1571,6 +1571,7 @@ module BABYLON.GLTF1 {
         public onMeshLoadedObservable = new Observable<AbstractMesh>();
         public onTextureLoadedObservable = new Observable<BaseTexture>();
         public onMaterialLoadedObservable = new Observable<Material>();
+        public onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
         public onCompleteObservable = new Observable<IGLTFLoader>();
         public onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
 

+ 16 - 19
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -16,33 +16,30 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRMaterialsPbrSpecularGlossiness>(context, material, (context, extension) => {
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
 
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial!;
-                    return material._loaded;
-                }
-
-                const promises = new Array<Promise<void>>();
+                if (!material._loaded) {
+                    const promises = new Array<Promise<void>>();
 
-                const babylonMaterial = this._loader._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
+                    const babylonMaterial = this._loader._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
 
-                promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadSpecularGlossinessPropertiesAsync(this._loader, context, material, extension));
+                    promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
 
-                this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-
-                babylonMesh.material = babylonMaterial;
+                    this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(() => {});
+                }
 
-                return (material._loaded = Promise.all(promises).then(() => {}));
+                assign(material._babylonMaterial!);
+                return material._loaded;
             });
         }
 
-        private _loadSpecularGlossinessPropertiesAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): Promise<void> {
+        private _loadSpecularGlossinessPropertiesAsync(context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): Promise<void> {
             const promises = new Array<Promise<void>>();
 
             const babylonMaterial = material._babylonMaterial as PBRMaterial;
@@ -59,13 +56,13 @@ module BABYLON.GLTF2.Extensions {
             babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
 
             if (properties.diffuseTexture) {
-                promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, texture => {
+                promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, texture => {
                     babylonMaterial.albedoTexture = texture;
                 }));
             }
 
             if (properties.specularGlossinessTexture) {
-                promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, texture => {
+                promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, texture => {
                     babylonMaterial.reflectivityTexture = texture;
                 }));
 
@@ -73,7 +70,7 @@ module BABYLON.GLTF2.Extensions {
                 babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
             }
 
-            loader._loadMaterialAlphaProperties(context, material);
+            this._loader._loadMaterialAlphaProperties(context, material);
 
             return Promise.all(promises).then(() => {});
         }

+ 6 - 2
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -68,7 +68,7 @@ module BABYLON.GLTF2.Extensions {
             });
         }
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             // Don't load material LODs if already loading a node LOD.
             if (this._loadingNodeLOD) {
                 return null;
@@ -89,7 +89,11 @@ module BABYLON.GLTF2.Extensions {
                         }
                     }
 
-                    const promise = this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(() => {
+                    const promise = this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : () => {}).then(() => {
+                        if (indexLOD !== 0) {
+                            assign(materialLOD._babylonMaterial!);
+                        }
+
                         if (indexLOD !== materialLODs.length - 1) {
                             const materialIndex = materialLODs[indexLOD + 1]._index;
                             if (this._loadMaterialSignals[materialIndex]) {

+ 30 - 23
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -54,6 +54,7 @@ module BABYLON.GLTF2 {
         public readonly onMeshLoadedObservable = new Observable<AbstractMesh>();
         public readonly onTextureLoadedObservable = new Observable<BaseTexture>();
         public readonly onMaterialLoadedObservable = new Observable<Material>();
+        public readonly onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
         public readonly onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
         public readonly onCompleteObservable = new Observable<IGLTFLoader>();
 
@@ -143,9 +144,13 @@ module BABYLON.GLTF2 {
                     promises.push(this._compileShadowGeneratorsAsync());
                 }
 
-                return Promise.all(promises).then(() => {
+                const resultPromise = Promise.all(promises).then(() => {
                     this._state = GLTFLoaderState.Ready;
                     this._startAnimations();
+                });
+
+                resultPromise.then(() => {
+                    this._rootBabylonMesh.setEnabled(true);
 
                     Tools.SetImmediate(() => {
                         if (!this._disposed) {
@@ -161,6 +166,8 @@ module BABYLON.GLTF2 {
                         }
                     });
                 });
+
+                return resultPromise;
             }).catch(error => {
                 Tools.Error("glTF Loader: " + error.message);
                 this._clear();
@@ -245,6 +252,8 @@ module BABYLON.GLTF2 {
 
         private _createRootNode(): ILoaderNode {
             this._rootBabylonMesh = new Mesh("__root__", this._babylonScene);
+            this._rootBabylonMesh.setEnabled(false);
+
             const rootNode = { _babylonMesh: this._rootBabylonMesh } as ILoaderNode;
             switch (this.coordinateSystemMode) {
                 case GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -436,7 +445,6 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
 
             const babylonMesh = new Mesh((mesh.name || node._babylonMesh!.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-            babylonMesh.setEnabled(false);
 
             node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
             node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
@@ -453,14 +461,14 @@ module BABYLON.GLTF2 {
             }
             else {
                 const material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, babylonMaterial => {
+                    babylonMesh.material = babylonMaterial;
+                }));
             }
 
             this.onMeshLoadedObservable.notifyObservers(babylonMesh);
 
-            return Promise.all(promises).then(() => {
-                babylonMesh.setEnabled(true);
-            });
+            return Promise.all(promises).then(() => {});
         }
 
         private _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<VertexData> {
@@ -813,6 +821,8 @@ module BABYLON.GLTF2 {
                 promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
             }
 
+            this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
+
             return Promise.all(promises).then(() => {
                 babylonAnimationGroup.normalize();
             });
@@ -820,7 +830,7 @@ module BABYLON.GLTF2 {
 
         private _loadAnimationChannelAsync(context: string, animationContext: string, animation: ILoaderAnimation, channel: ILoaderAnimationChannel, babylonAnimationGroup: AnimationGroup): Promise<void> {
             const targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-            if (!targetNode._babylonMesh) {
+            if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                 return Promise.resolve();
             }
 
@@ -1177,8 +1187,8 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
         }
 
-        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void> {
+            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
             if (promise) {
                 return promise;
             }
@@ -1186,24 +1196,21 @@ module BABYLON.GLTF2 {
             material._babylonMeshes = material._babylonMeshes || [];
             material._babylonMeshes.push(babylonMesh);
 
-            if (material._loaded) {
-                babylonMesh.material = material._babylonMaterial!;
-                return material._loaded;
-            }
-
-            const promises = new Array<Promise<void>>();
-
-            const babylonMaterial = this._createMaterial(material);
-            material._babylonMaterial = babylonMaterial;
+            if (!material._loaded) {
+                const promises = new Array<Promise<void>>();
 
-            promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-            promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                const babylonMaterial = this._createMaterial(material);
+                material._babylonMaterial = babylonMaterial;
 
-            this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
 
-            babylonMesh.material = babylonMaterial;
+                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                material._loaded = Promise.all(promises).then(() => {});
+            }
 
-            return (material._loaded = Promise.all(promises).then(() => {}));
+            assign(material._babylonMaterial!);
+            return material._loaded;
         }
 
         public _createMaterial(material: ILoaderMaterial): PBRMaterial {

+ 3 - 3
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -27,7 +27,7 @@ module BABYLON.GLTF2 {
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> { return null; }
 
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> { return null; }
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
 
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> { return null; }
@@ -75,8 +75,8 @@ module BABYLON.GLTF2 {
         }
 
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh));
+        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, assign));
         }
 
         /** Helper method called by the loader to allow extensions to override loading uris. */

+ 28 - 2
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -63,6 +63,7 @@ module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -150,7 +151,7 @@ module BABYLON {
         public readonly onTextureLoadedObservable = new Observable<BaseTexture>();
 
         private _onTextureLoadedObserver: Nullable<Observer<BaseTexture>>;
-        public set onTextureLoaded(callback: (Texture: BaseTexture) => void) {
+        public set onTextureLoaded(callback: (texture: BaseTexture) => void) {
             if (this._onTextureLoadedObserver) {
                 this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
             }
@@ -163,7 +164,7 @@ module BABYLON {
         public readonly onMaterialLoadedObservable = new Observable<Material>();
 
         private _onMaterialLoadedObserver: Nullable<Observer<Material>>;
-        public set onMaterialLoaded(callback: (Material: Material) => void) {
+        public set onMaterialLoaded(callback: (material: Material) => void) {
             if (this._onMaterialLoadedObserver) {
                 this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
             }
@@ -171,6 +172,19 @@ module BABYLON {
         }
 
         /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
+         */
+        public readonly onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
+
+        private _onAnimationGroupLoadedObserver: Nullable<Observer<AnimationGroup>>;
+        public set onAnimationGroupLoaded(callback: (animationGroup: AnimationGroup) => void) {
+            if (this._onAnimationGroupLoadedObserver) {
+                this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+            }
+            this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+        }
+
+        /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -213,6 +227,18 @@ module BABYLON {
         }
 
         /**
+         * Gets a promise that resolves when the asset is completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        public whenCompleteAsync(): Promise<void> {
+            return new Promise(resolve => {
+                this.onCompleteObservable.add(() => {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        }
+
+        /**
          * The loader state or null if not active.
          */
         public get loaderState(): Nullable<GLTFLoaderState> {

+ 27 - 1
src/Animations/babylon.animationGroup.ts

@@ -59,6 +59,13 @@ module BABYLON {
             return this._targetedAnimations;
         }
 
+        /**
+         * returning the list of animatables controlled by this animation group.
+         */
+        public get animatables(): Array<Animatable> {
+            return this._animatables;
+        }
+
         public constructor(public name: string, scene: Nullable<Scene> = null) {
             this._scene = scene || Engine.LastCreatedScene!;
 
@@ -245,6 +252,25 @@ module BABYLON {
         }
 
         /**
+         * Goes to a specific frame in this animation group
+         * 
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        public goToFrame(frame: number): AnimationGroup {
+            if (!this._isStarted) {
+                return this;
+            }
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+
+            return this;
+        }
+
+        /**
          * Dispose all associated resources
          */
         public dispose(): void {
@@ -258,4 +284,4 @@ module BABYLON {
             }
         }
     }
-}
+}

+ 13 - 2
src/Helpers/babylon.environmentHelper.ts

@@ -310,6 +310,12 @@ module BABYLON {
         private _options: IEnvironmentHelperOptions;
 
         /**
+         * This observable will be notified with any error during the creation of the environment, 
+         * mainly texture creation errors.
+         */
+        public onErrorObservable: Observable<{ message?: string, exception?: any }>;
+
+        /**
          * constructor
          * @param options 
          * @param scene The scene to add the material to
@@ -320,6 +326,7 @@ module BABYLON {
                 ...options
             }
             this._scene = scene;
+            this.onErrorObservable = new Observable();
 
             this._setupBackground();
             this._setupImageProcessing();
@@ -557,7 +564,7 @@ module BABYLON {
                 return;
             }
 
-            const diffuseTexture = new Texture(this._options.groundTexture, this._scene);
+            const diffuseTexture = new Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -664,12 +671,16 @@ module BABYLON {
                 return;
             }
 
-            this._skyboxTexture = new CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
         }
 
+        private _errorHandler = (message?: string, exception?: any) => {
+            this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+        }
+
         /**
          * Dispose all the elements created by the Helper.
          */

+ 23 - 0
src/Tools/babylon.promise.ts

@@ -196,6 +196,29 @@ module BABYLON {
 
             return newPromise;
         }
+
+        public static race<T>(promises: InternalPromise<T>[]): InternalPromise<T> {
+            let newPromise: Nullable<InternalPromise<T>> = new InternalPromise();
+
+            if (promises.length) {
+                for (const promise of promises) {
+                    promise.then((value?: Nullable<T>) => {
+                        if (newPromise) {
+                            newPromise._resolve(value);
+                            newPromise = null;
+                        }
+                        return null;
+                    }, (reason: any) => {
+                        if (newPromise) {
+                            newPromise._reject(reason);
+                            newPromise = null;
+                        }
+                    });
+                }
+            }
+
+            return newPromise;
+        }
     }
 
     /**

+ 74 - 52
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -69,7 +69,8 @@ describe('Babylon Scene Loader', function () {
             let textureCounts: { [name: string]: number } = {};
             let ready = false;
 
-            const deferred = new BABYLON.Deferred();
+            const promises = new Array<Promise<void>>();
+
             BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
                 loader.onParsed = data => {
                     parsedCount++;
@@ -86,19 +87,13 @@ describe('Babylon Scene Loader', function () {
                     textureCounts[texture.name]++;
                 };
 
-                loader.onComplete = () => {
-                    try {
-                        expect(ready, "ready").to.be.true;
-                        deferred.resolve();
-                    }
-                    catch (e) {
-                        deferred.reject(e);
-                    }
-                };
+                promises.push(loader.whenCompleteAsync().then(() => {
+                    expect(ready, "ready").to.be.true;
+                }));
             }, undefined, undefined, undefined, true);
 
             const scene = new BABYLON.Scene(subject);
-            const promise = BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
                 ready = true;
 
                 expect(parsedCount, "parsedCount").to.equal(1);
@@ -115,70 +110,88 @@ describe('Babylon Scene Loader', function () {
                 for (const textureName in expectedTextureLoadCounts) {
                     expect(textureCounts, "textureCounts").to.have.property(textureName, expectedTextureLoadCounts[textureName]);
                 }
-            });
+            }));
 
-            return Promise.all([promise, deferred.promise]);
+            return Promise.all(promises);
         });
 
         it('Load BoomBox with dispose', () => {
             let ready = false;
             let disposed = false;
 
-            const deferred = new BABYLON.Deferred<void>();
+            const promises = new Array<Promise<void>>();
+
             BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
                 loader.onDispose = () => {
                     disposed = true;
                 };
 
-                BABYLON.Tools.DelayAsync(50).then(() => {
+                promises.push(BABYLON.Tools.DelayAsync(50).then(() => {
                     loader.dispose();
                     expect(ready, "ready").to.be.false;
                     expect(disposed, "disposed").to.be.true;
-                    deferred.resolve();
-                }).catch(error => {
-                    deferred.reject(error);
-                });
+                }));
             }, undefined, undefined, undefined, true);
 
             const scene = new BABYLON.Scene(subject);
-            BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox2.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox2.gltf", scene).then(() => {
                 ready = true;
-            }).catch(error => {
-                // Cannot rely on the typical error handling of promises since the AppendAsync is not
-                // supposed to complete because the loader is being disposed.
-                deferred.reject(error);
-            });
+            }));
 
-            return deferred.promise;
+            return Promise.race(promises);
         });
 
         it('Load BoomBox with compileMaterials', () => {
             let createShaderProgramSpy: sinon.SinonSpy;
 
-            const deferred = new BABYLON.Deferred();
+            const promises = new Array<Promise<void>>();
+
             BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
                 loader.compileMaterials = true;
 
-                loader.onComplete = () => {
+                promises.push(loader.whenCompleteAsync().then(() => {
                     try {
                         expect(createShaderProgramSpy.called, "createShaderProgramSpy.called").to.be.false;
-                        deferred.resolve();
-                    }
-                    catch (e) {
-                        deferred.reject(e);
                     }
                     finally {
                         createShaderProgramSpy.restore();
                     }
-                };
+                }));
             }, undefined, undefined, undefined, true);
 
             const scene = new BABYLON.Scene(subject);
-            const promise = BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(() => {
                 createShaderProgramSpy = sinon.spy(subject, "createShaderProgram");
+            }));
+
+            promises.push(scene.whenReadyAsync());
+
+            return Promise.all(promises);
+        });
+
+        it('Load BoomBox with rootMesh.isEnabled check', () => {
+            const scene = new BABYLON.Scene(subject);
+            let rootMesh: BABYLON.AbstractMesh;
+
+            subject.runRenderLoop(() => {
+                if (!rootMesh) {
+                    for (const mesh of scene.meshes) {
+                        if (!mesh.parent) {
+                            rootMesh = mesh;
+                            break;
+                        }
+                    }
+                }
+
+                if (rootMesh) {
+                    expect(rootMesh.isEnabled(), "rootMesh.isEnabled").to.be.false;
+                }
             });
 
-            return Promise.all([promise, deferred.promise, scene.whenReadyAsync()]);
+            return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BoomBox/", "BoomBox.gltf", scene).then(scene => {
+                expect(rootMesh.isEnabled(), "rootMesh.isEnabled").to.be.true;
+                subject.stopRenderLoop();
+            });
         });
 
         it('Load Alien', () => {
@@ -204,31 +217,36 @@ describe('Babylon Scene Loader', function () {
             });
         });
 
-        it('Load TwoQuads', () => {
+        it('Load TwoQuads with LODs', () => {
+            const scene = new BABYLON.Scene(subject);
+            const promises = new Array<Promise<void>>();
             const materials: { [name: string]: BABYLON.Material } = {};
 
-            const deferred = new BABYLON.Deferred();
+            subject.runRenderLoop(() => {
+                for (const mesh of scene.meshes) {
+                    if (mesh.material && mesh.isEnabled()) {
+                        expect(mesh.material.getActiveTextures().every(texture => texture.isReady()), "active mesh material textures are ready").to.be.true;
+                    }
+                }
+            });
+
             BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
                 loader.onMaterialLoaded = material => {
                     expect(materials[material.name], `materials["${material.name}"]`).to.be.undefined;
                     materials[material.name] = material;
                 };
 
-                loader.onComplete = () => {
-                    try {
-                        expect(materials["LOD0"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 0 ready").to.be.true;
-                        expect(materials["LOD1"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 1 ready").to.be.true;
-                        expect(materials["LOD2"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 2 ready").to.be.true;
-                        deferred.resolve();
-                    }
-                    catch (e) {
-                        deferred.reject(e);
-                    }
-                };
+                promises.push(loader.whenCompleteAsync().then(() => {
+                    expect(materials["LOD0"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 0 ready").to.be.true;
+                    expect(materials["LOD1"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 1 ready").to.be.true;
+                    expect(materials["LOD2"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 2 ready").to.be.true;
+
+                    expect(scene.getMeshByName("node0_0").material.name, "node 0 primitive 0 material").to.equal("LOD0");
+                    expect(scene.getMeshByName("node1_0").material.name, "node 1 primitive 0 material").to.equal("LOD0");
+                }));
             }, undefined, undefined, undefined, true);
 
-            const scene = new BABYLON.Scene(subject);
-            const promise = BABYLON.SceneLoader.AppendAsync("/Playground/scenes/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
                 expect(Object.keys(materials), "materials").to.have.lengthOf(3);
 
                 expect(materials["LOD0"].getActiveTextures(), "material LOD 0 active textures").to.have.lengthOf(1);
@@ -238,11 +256,15 @@ describe('Babylon Scene Loader', function () {
                 expect(materials["LOD0"].getActiveTextures().some(texture => texture.isReady()), "Some textures of LOD 0 ready").to.be.false;
                 expect(materials["LOD1"].getActiveTextures().some(texture => texture.isReady()), "Some textures of LOD 1 ready").to.be.false;
                 expect(materials["LOD2"].getActiveTextures().every(texture => texture.isReady()), "All textures of LOD 2 ready").to.be.true;
-            });
 
-            return Promise.all([promise, deferred.promise]);
+                expect(scene.getMeshByName("node0_0").material.name, "node 0 primitive 0 material").to.equal("LOD2");
+                expect(scene.getMeshByName("node1_0").material.name, "node 1 primitive 0 material").to.equal("LOD2");
+            }));
+
+            return Promise.all(promises);
         });
 
+        // TODO: test animation group callback
         // TODO: test material instancing
         // TODO: test ImportMesh with specific node name
         // TODO: test KHR_materials_pbrSpecularGlossiness