Bläddra i källkod

Merge pull request #2892 from BabylonJS/master

Nightly
David Catuhe 8 år sedan
förälder
incheckning
8f9a9ec2ee
39 ändrade filer med 13275 tillägg och 13075 borttagningar
  1. 1507 1507
      dist/preview release/babylon.d.ts
  2. 39 39
      dist/preview release/babylon.js
  3. 9 3
      dist/preview release/babylon.max.js
  4. 1507 1507
      dist/preview release/babylon.module.d.ts
  5. 40 40
      dist/preview release/babylon.worker.js
  6. 4234 4234
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  7. 42 42
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  8. 243 210
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  9. 4234 4234
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  10. 3 3
      dist/preview release/gui/babylon.gui.min.js
  11. 263 263
      dist/preview release/inspector/babylon.inspector.bundle.js
  12. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  13. 8 4
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  14. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  15. 37 33
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  16. 234 207
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  17. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  18. 37 33
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  19. 234 207
      dist/preview release/loaders/babylon.glTFFileLoader.js
  20. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  21. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  22. 234 207
      dist/preview release/loaders/babylonjs.loaders.js
  23. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  24. 37 33
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  25. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  26. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  27. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  28. 3 3
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  29. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  30. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  31. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  32. 3 1
      dist/preview release/what's new.md
  33. 10 18
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  34. 57 39
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  35. 185 179
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  36. 35 2
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  37. 11 4
      loaders/src/glTF/babylon.glTFFileLoader.ts
  38. 2 2
      src/Physics/Plugins/babylon.cannonJSPlugin.ts
  39. 7 1
      src/PostProcess/babylon.volumetricLightScatteringPostProcess.ts

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1507 - 1507
dist/preview release/babylon.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 39 - 39
dist/preview release/babylon.js


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

@@ -56527,7 +56527,13 @@ var BABYLON;
             this._volumetricLightScatteringRTT.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this._volumetricLightScatteringRTT.renderList = null;
             this._volumetricLightScatteringRTT.renderParticles = false;
-            scene.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            var camera = this.getCamera();
+            if (camera) {
+                camera.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            }
+            else {
+                scene.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            }
             // Custom render function for submeshes
             var renderSubMesh = function (subMesh) {
                 var mesh = subMesh.getRenderingMesh();
@@ -62933,9 +62939,9 @@ var BABYLON;
                         }
                     }
                     currentRotation.multiplyInPlace(mesh.rotationQuaternion);
-                    mesh.getChildMeshes(true).forEach(processMesh.bind(_this, mesh.getAbsolutePosition()));
+                    mesh.getChildMeshes(true).filter(function (m) { return !!m.physicsImpostor; }).forEach(processMesh.bind(_this, mesh.getAbsolutePosition()));
                 };
-                meshChildren.forEach(processMesh.bind(this, mainImpostor.object.getAbsolutePosition()));
+                meshChildren.filter(function (m) { return !!m.physicsImpostor; }).forEach(processMesh.bind(this, mainImpostor.object.getAbsolutePosition()));
             }
         };
         CannonJSPlugin.prototype.removePhysicsBody = function (impostor) {

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1507 - 1507
dist/preview release/babylon.module.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 40 - 40
dist/preview release/babylon.worker.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4234 - 4234
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 42 - 42
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 243 - 210
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -56527,7 +56527,13 @@ var BABYLON;
             this._volumetricLightScatteringRTT.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this._volumetricLightScatteringRTT.renderList = null;
             this._volumetricLightScatteringRTT.renderParticles = false;
-            scene.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            var camera = this.getCamera();
+            if (camera) {
+                camera.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            }
+            else {
+                scene.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            }
             // Custom render function for submeshes
             var renderSubMesh = function (subMesh) {
                 var mesh = subMesh.getRenderingMesh();
@@ -62933,9 +62939,9 @@ var BABYLON;
                         }
                     }
                     currentRotation.multiplyInPlace(mesh.rotationQuaternion);
-                    mesh.getChildMeshes(true).forEach(processMesh.bind(_this, mesh.getAbsolutePosition()));
+                    mesh.getChildMeshes(true).filter(function (m) { return !!m.physicsImpostor; }).forEach(processMesh.bind(_this, mesh.getAbsolutePosition()));
                 };
-                meshChildren.forEach(processMesh.bind(this, mainImpostor.object.getAbsolutePosition()));
+                meshChildren.filter(function (m) { return !!m.physicsImpostor; }).forEach(processMesh.bind(this, mainImpostor.object.getAbsolutePosition()));
             }
         };
         CannonJSPlugin.prototype.removePhysicsBody = function (impostor) {
@@ -75578,17 +75584,31 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        var GLTFLoaderTracker = (function () {
+            function GLTFLoaderTracker(onComplete) {
+                this._pendingCount = 0;
+                this._callback = onComplete;
+            }
+            GLTFLoaderTracker.prototype._addPendingData = function (data) {
+                this._pendingCount++;
+            };
+            GLTFLoaderTracker.prototype._removePendingData = function (data) {
+                if (--this._pendingCount === 0) {
+                    this._callback();
+                }
+            };
+            return GLTFLoaderTracker;
+        }());
         var GLTFLoader = (function () {
             function GLTFLoader(parent) {
                 this._renderReady = false;
                 this._disposed = false;
-                this._blockPendingTracking = false;
-                // Observable with boolean indicating success or error.
                 this._renderReadyObservable = new BABYLON.Observable();
                 // Count of pending work that needs to complete before the asset is rendered.
                 this._renderPendingCount = 0;
-                // Count of pending work that needs to complete before the loader is cleared.
+                // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
+                this._loaderTrackers = new Array();
                 this._parent = parent;
             }
             GLTFLoader.RegisterExtension = function (extension) {
@@ -75600,28 +75620,6 @@ var BABYLON;
                 // Keep the order of registration so that extensions registered first are called first.
                 GLTF2.GLTFLoaderExtension._Extensions.push(extension);
             };
-            Object.defineProperty(GLTFLoader.prototype, "gltf", {
-                get: function () {
-                    return this._gltf;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            Object.defineProperty(GLTFLoader.prototype, "babylonScene", {
-                get: function () {
-                    return this._babylonScene;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.executeWhenRenderReady = function (func) {
-                if (this._renderReady) {
-                    func();
-                }
-                else {
-                    this._renderReadyObservable.add(func);
-                }
-            };
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
@@ -75662,10 +75660,10 @@ var BABYLON;
                 this._successCallback = onSuccess;
                 this._progressCallback = onProgress;
                 this._errorCallback = onError;
-                this.addPendingData(this);
+                this._addPendingData(this);
                 this._loadScene(nodeNames);
                 this._loadAnimations();
-                this.removePendingData(this);
+                this._removePendingData(this);
             };
             GLTFLoader.prototype._onError = function (message) {
                 if (this._errorCallback) {
@@ -75674,39 +75672,33 @@ var BABYLON;
                 this.dispose();
             };
             GLTFLoader.prototype._onProgress = function (event) {
-                this._progressCallback(event);
+                if (this._progressCallback) {
+                    this._progressCallback(event);
+                }
             };
-            GLTFLoader.prototype._onRenderReady = function () {
-                switch (this._parent.coordinateSystemMode) {
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
-                        if (!this._babylonScene.useRightHandedSystem) {
-                            this._addRightHandToLeftHandRootTransform();
-                        }
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
-                        // do nothing
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
-                        this._babylonScene.useRightHandedSystem = true;
-                        break;
-                    default:
-                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
-                        break;
+            GLTFLoader.prototype._executeWhenRenderReady = function (func) {
+                if (this._renderReady) {
+                    func();
+                }
+                else {
+                    this._renderReadyObservable.add(func);
                 }
-                this._showMeshes();
+            };
+            GLTFLoader.prototype._onRenderReady = function () {
+                var rootNode = this._gltf.nodes[this._gltf.nodes.length - 1];
+                rootNode.babylonMesh.setEnabled(true);
                 this._startAnimations();
                 this._successCallback();
                 this._renderReadyObservable.notifyObservers(this);
+                if (this._parent.onReady) {
+                    this._parent.onReady();
+                }
             };
-            GLTFLoader.prototype._onLoaderComplete = function () {
+            GLTFLoader.prototype._onComplete = function () {
                 if (this._parent.onComplete) {
                     this._parent.onComplete();
                 }
-            };
-            GLTFLoader.prototype._onLoaderFirstLODComplete = function () {
-                if (this._parent.onFirstLODComplete) {
-                    this._parent.onFirstLODComplete();
-                }
+                this.dispose();
             };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
@@ -75727,26 +75719,8 @@ var BABYLON;
                     binaryBuffer.loadedData = data.bin;
                 }
             };
-            GLTFLoader.prototype._addRightHandToLeftHandRootTransform = function () {
-                this._rootMesh = new BABYLON.Mesh("root", this._babylonScene);
-                this._rootMesh.isVisible = false;
-                this._rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
-                this._rootMesh.rotation.y = Math.PI;
-                var nodes = this._gltf.nodes;
-                if (nodes) {
-                    for (var i = 0; i < nodes.length; i++) {
-                        var mesh = nodes[i].babylonMesh;
-                        if (mesh && !mesh.parent) {
-                            mesh.parent = this._rootMesh;
-                        }
-                    }
-                }
-            };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = [];
-                if (this._rootMesh) {
-                    meshes.push(this._rootMesh);
-                }
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     nodes.forEach(function (node) {
@@ -75779,16 +75753,34 @@ var BABYLON;
                 }
                 return targets;
             };
-            GLTFLoader.prototype._showMeshes = function () {
-                this._getMeshes().forEach(function (mesh) { return mesh.isVisible = true; });
-            };
             GLTFLoader.prototype._startAnimations = function () {
                 var _this = this;
                 this._getAnimationTargets().forEach(function (target) { return _this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true); });
             };
             GLTFLoader.prototype._loadScene = function (nodeNames) {
-                var _this = this;
                 var scene = this._gltf.scenes[this._gltf.scene || 0];
+                var rootNode = { name: "root", children: scene.nodes };
+                switch (this._parent.coordinateSystemMode) {
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
+                        if (!this._babylonScene.useRightHandedSystem) {
+                            rootNode.rotation = [0, 1, 0, 0];
+                            rootNode.scale = [1, 1, -1];
+                        }
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
+                        // do nothing
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
+                        this._babylonScene.useRightHandedSystem = true;
+                        break;
+                    default:
+                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
+                        break;
+                }
+                // Inject a root node into the scene.
+                this._gltf.nodes = this._gltf.nodes || [];
+                scene.nodes = [this._gltf.nodes.length];
+                this._gltf.nodes.push(rootNode);
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(nodeIndices, function (node, index, parentNode) {
                     node.index = index;
@@ -75805,19 +75797,25 @@ var BABYLON;
                     }
                     var filteredNodeIndices = new Array();
                     this._traverseNodes(nodeIndices, function (node) {
-                        if (nodeNames.indexOf(node.name) === -1) {
-                            return true;
+                        if (nodeNames.indexOf(node.name) !== -1) {
+                            filteredNodeIndices.push(node.index);
+                            return false;
                         }
-                        filteredNodeIndices.push(node.index);
-                        return false;
+                        return true;
                     });
                     nodeIndices = filteredNodeIndices;
                 }
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadNode(node); });
+                for (var i = 0; i < nodeIndices.length; i++) {
+                    this._loadNode(this._gltf.nodes[nodeIndices[i]]);
+                }
+                // Disable the root mesh until the asset is ready to render.
+                rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (node) {
+                if (GLTF2.GLTFLoaderExtension.LoadNode(this, node)) {
+                    return;
+                }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.isVisible = false;
                 this._loadTransform(node);
                 if (node.mesh !== undefined) {
                     var mesh = this._gltf.meshes[node.mesh];
@@ -75834,7 +75832,11 @@ var BABYLON;
                 if (node.camera !== undefined) {
                     // TODO: handle cameras
                 }
-                return true;
+                if (node.children) {
+                    for (var i = 0; i < node.children.length; i++) {
+                        this._loadNode(this._gltf.nodes[node.children[i]]);
+                    }
+                }
             };
             GLTFLoader.prototype._loadMesh = function (node, mesh) {
                 var _this = this;
@@ -75866,25 +75868,24 @@ var BABYLON;
                             loadMaterial: function () {
                                 if (primitive.material === undefined) {
                                     babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
+                                    return;
                                 }
-                                else {
-                                    var material = _this._gltf.materials[primitive.material];
-                                    _this.loadMaterial(material, function (babylonMaterial, isNew) {
-                                        if (isNew && _this._parent.onMaterialLoaded) {
-                                            _this._parent.onMaterialLoaded(babylonMaterial);
-                                        }
-                                        if (_this._parent.onBeforeMaterialReadyAsync) {
-                                            _this.addLoaderPendingData(material);
-                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
-                                                babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                                _this.removeLoaderPendingData(material);
-                                            });
-                                        }
-                                        else {
+                                var material = _this._gltf.materials[primitive.material];
+                                _this._loadMaterial(material, function (babylonMaterial, isNew) {
+                                    if (isNew && _this._parent.onMaterialLoaded) {
+                                        _this._parent.onMaterialLoaded(babylonMaterial);
+                                    }
+                                    if (_this._parent.onBeforeMaterialReadyAsync) {
+                                        _this._addLoaderPendingData(material);
+                                        _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
                                             babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                        }
-                                    });
-                                }
+                                            _this._removeLoaderPendingData(material);
+                                        });
+                                    }
+                                    else {
+                                        babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                    }
+                                });
                             }
                         });
                         vertexData.merge(subVertexData);
@@ -76115,14 +76116,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._traverseNode = function (index, action, parentNode) {
                 if (parentNode === void 0) { parentNode = null; }
+                if (GLTF2.GLTFLoaderExtension.TraverseNode(this, index, action, parentNode)) {
+                    return;
+                }
                 var node = this._gltf.nodes[index];
                 if (!action(node, index, parentNode)) {
                     return;
                 }
                 if (node.children) {
-                    for (var i = 0; i < node.children.length; i++) {
-                        this._traverseNode(node.children[i], action, node);
-                    }
+                    this._traverseNodes(node.children, action, node);
                 }
             };
             GLTFLoader.prototype._loadAnimations = function () {
@@ -76250,11 +76252,11 @@ var BABYLON;
             GLTFLoader.prototype._loadBufferAsync = function (index, onSuccess) {
                 var _this = this;
                 var buffer = this._gltf.buffers[index];
-                this.addPendingData(buffer);
+                this._addPendingData(buffer);
                 if (buffer.loadedData) {
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(buffer.uri)) {
@@ -76262,20 +76264,20 @@ var BABYLON;
                     buffer.loadedData = new Uint8Array(data);
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (buffer.loadedObservable) {
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else {
                     buffer.loadedObservable = new BABYLON.Observable();
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                     BABYLON.Tools.LoadFile(this._rootUrl + buffer.uri, function (data) {
                         buffer.loadedData = new Uint8Array(data);
@@ -76288,7 +76290,7 @@ var BABYLON;
                     }, this._babylonScene.database, true, function (request) {
                         if (!_this._disposed) {
                             _this._onError("Failed to load file '" + buffer.uri + "': " + request.status + " " + request.statusText);
-                            _this.removePendingData(buffer);
+                            _this._removePendingData(buffer);
                         }
                     });
                 }
@@ -76417,54 +76419,42 @@ var BABYLON;
                         return 0;
                 }
             };
-            Object.defineProperty(GLTFLoader.prototype, "blockPendingTracking", {
-                set: function (value) {
-                    this._blockPendingTracking = value;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.addPendingData = function (data) {
+            GLTFLoader.prototype._addPendingData = function (data) {
                 if (!this._renderReady) {
                     this._renderPendingCount++;
                 }
-                this.addLoaderPendingData(data);
+                this._addLoaderPendingData(data);
             };
-            GLTFLoader.prototype.removePendingData = function (data) {
+            GLTFLoader.prototype._removePendingData = function (data) {
                 if (!this._renderReady) {
                     if (--this._renderPendingCount === 0) {
                         this._renderReady = true;
                         this._onRenderReady();
                     }
                 }
-                this.removeLoaderPendingData(data);
-            };
-            GLTFLoader.prototype.addLoaderNonBlockingPendingData = function (data) {
-                if (!this._nonBlockingData) {
-                    this._nonBlockingData = new Array();
-                }
-                this._nonBlockingData.push(data);
+                this._removeLoaderPendingData(data);
             };
-            GLTFLoader.prototype.addLoaderPendingData = function (data) {
-                if (this._blockPendingTracking) {
-                    this.addLoaderNonBlockingPendingData(data);
-                    return;
-                }
+            GLTFLoader.prototype._addLoaderPendingData = function (data) {
                 this._loaderPendingCount++;
+                this._loaderTrackers.forEach(function (tracker) { return tracker._addPendingData(data); });
             };
-            GLTFLoader.prototype.removeLoaderPendingData = function (data) {
-                var indexInPending = this._nonBlockingData ? this._nonBlockingData.indexOf(data) : -1;
-                if (indexInPending !== -1) {
-                    this._nonBlockingData.splice(indexInPending, 1);
-                }
-                else if (--this._loaderPendingCount === 0) {
-                    this._onLoaderFirstLODComplete();
-                }
-                if ((!this._nonBlockingData || this._nonBlockingData.length === 0) && this._loaderPendingCount === 0) {
-                    this._onLoaderComplete();
-                    this.dispose();
+            GLTFLoader.prototype._removeLoaderPendingData = function (data) {
+                this._loaderTrackers.forEach(function (tracker) { return tracker._removePendingData(data); });
+                if (--this._loaderPendingCount === 0) {
+                    this._onComplete();
                 }
             };
+            GLTFLoader.prototype._whenAction = function (action, onComplete) {
+                var _this = this;
+                var tracker = new GLTFLoaderTracker(function () {
+                    _this._loaderTrackers.splice(_this._loaderTrackers.indexOf(tracker));
+                    onComplete();
+                });
+                this._loaderTrackers.push(tracker);
+                this._addLoaderPendingData(tracker);
+                action();
+                this._removeLoaderPendingData(tracker);
+            };
             GLTFLoader.prototype._getDefaultMaterial = function () {
                 if (!this._defaultMaterial) {
                     var id = "__gltf_default";
@@ -76492,17 +76482,17 @@ var BABYLON;
                 babylonMaterial.metallic = properties.metallicFactor === undefined ? 1 : properties.metallicFactor;
                 babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
                 if (properties.baseColorTexture) {
-                    babylonMaterial.albedoTexture = this.loadTexture(properties.baseColorTexture);
+                    babylonMaterial.albedoTexture = this._loadTexture(properties.baseColorTexture);
                 }
                 if (properties.metallicRoughnessTexture) {
-                    babylonMaterial.metallicTexture = this.loadTexture(properties.metallicRoughnessTexture);
+                    babylonMaterial.metallicTexture = this._loadTexture(properties.metallicRoughnessTexture);
                     babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
                     babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
                     babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
                 }
-                this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
+                this._loadMaterialAlphaProperties(material, properties.baseColorFactor);
             };
-            GLTFLoader.prototype.loadMaterial = function (material, assign) {
+            GLTFLoader.prototype._loadMaterial = function (material, assign) {
                 if (material.babylonMaterial) {
                     assign(material.babylonMaterial, false);
                     return;
@@ -76510,17 +76500,17 @@ var BABYLON;
                 if (GLTF2.GLTFLoaderExtension.LoadMaterial(this, material, assign)) {
                     return;
                 }
-                this.createPbrMaterial(material);
-                this.loadMaterialBaseProperties(material);
+                this._createPbrMaterial(material);
+                this._loadMaterialBaseProperties(material);
                 this._loadMaterialMetallicRoughnessProperties(material);
                 assign(material.babylonMaterial, true);
             };
-            GLTFLoader.prototype.createPbrMaterial = function (material) {
+            GLTFLoader.prototype._createPbrMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "mat" + material.index, this._babylonScene);
                 babylonMaterial.sideOrientation = BABYLON.Material.CounterClockWiseSideOrientation;
                 material.babylonMaterial = babylonMaterial;
             };
-            GLTFLoader.prototype.loadMaterialBaseProperties = function (material) {
+            GLTFLoader.prototype._loadMaterialBaseProperties = function (material) {
                 var babylonMaterial = material.babylonMaterial;
                 babylonMaterial.emissiveColor = material.emissiveFactor ? BABYLON.Color3.FromArray(material.emissiveFactor) : new BABYLON.Color3(0, 0, 0);
                 if (material.doubleSided) {
@@ -76528,7 +76518,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
+                    babylonMaterial.bumpTexture = this._loadTexture(material.normalTexture);
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
                     babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
                     if (material.normalTexture.scale !== undefined) {
@@ -76536,17 +76526,17 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    babylonMaterial.ambientTexture = this.loadTexture(material.occlusionTexture);
+                    babylonMaterial.ambientTexture = this._loadTexture(material.occlusionTexture);
                     babylonMaterial.useAmbientInGrayScale = true;
                     if (material.occlusionTexture.strength !== undefined) {
                         babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
                     }
                 }
                 if (material.emissiveTexture) {
-                    babylonMaterial.emissiveTexture = this.loadTexture(material.emissiveTexture);
+                    babylonMaterial.emissiveTexture = this._loadTexture(material.emissiveTexture);
                 }
             };
-            GLTFLoader.prototype.loadMaterialAlphaProperties = function (material, colorFactor) {
+            GLTFLoader.prototype._loadMaterialAlphaProperties = function (material, colorFactor) {
                 var babylonMaterial = material.babylonMaterial;
                 var alphaMode = material.alphaMode || "OPAQUE";
                 switch (alphaMode) {
@@ -76569,7 +76559,7 @@ var BABYLON;
                 }
                 babylonMaterial.alphaCutOff = material.alphaCutoff === undefined ? 0.5 : material.alphaCutoff;
             };
-            GLTFLoader.prototype.loadTexture = function (textureInfo) {
+            GLTFLoader.prototype._loadTexture = function (textureInfo) {
                 var _this = this;
                 var texture = this._gltf.textures[textureInfo.index];
                 var texCoord = textureInfo.texCoord || 0;
@@ -76580,15 +76570,15 @@ var BABYLON;
                 var sampler = (texture.sampler === undefined ? {} : this._gltf.samplers[texture.sampler]);
                 var noMipMaps = (sampler.minFilter === GLTF2.ETextureMinFilter.NEAREST || sampler.minFilter === GLTF2.ETextureMinFilter.LINEAR);
                 var samplingMode = GLTF2.GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
-                this.addPendingData(texture);
+                this._addPendingData(texture);
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, noMipMaps, false, samplingMode, function () {
                     if (!_this._disposed) {
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 }, function () {
                     if (!_this._disposed) {
                         _this._onError("Failed to load texture '" + source.uri + "'");
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 });
                 if (texture.url) {
@@ -76755,9 +76745,34 @@ var BABYLON;
             function GLTFLoaderExtension() {
                 this.enabled = true;
             }
-            GLTFLoaderExtension.prototype.loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._traverseNode = function (loader, index, action, parentNode) { return false; };
+            GLTFLoaderExtension.prototype._loadNode = function (loader, node) { return false; };
+            GLTFLoaderExtension.prototype._loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._loadExtension = function (property, action) {
+                var _this = this;
+                if (!property.extensions) {
+                    return false;
+                }
+                var extension = property.extensions[this.name];
+                if (!extension) {
+                    return false;
+                }
+                // Clear out the extension before executing the action to avoid recursing into the same property.
+                property.extensions[this.name] = undefined;
+                action(extension, function () {
+                    // Restore the extension after completing the action.
+                    property.extensions[_this.name] = extension;
+                });
+                return true;
+            };
+            GLTFLoaderExtension.TraverseNode = function (loader, index, action, parentNode) {
+                return this._ApplyExtensions(function (extension) { return extension._traverseNode(loader, index, action, parentNode); });
+            };
+            GLTFLoaderExtension.LoadNode = function (loader, node) {
+                return this._ApplyExtensions(function (extension) { return extension._loadNode(loader, node); });
+            };
             GLTFLoaderExtension.LoadMaterial = function (loader, material, assign) {
-                return this._ApplyExtensions(function (extension) { return extension.loadMaterial(loader, material, assign); });
+                return this._ApplyExtensions(function (extension) { return extension._loadMaterial(loader, material, assign); });
             };
             GLTFLoaderExtension._ApplyExtensions = function (action) {
                 var extensions = GLTFLoaderExtension._Extensions;
@@ -76792,6 +76807,7 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // See https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod for more information about this extension.
             var MSFTLOD = (function (_super) {
                 __extends(MSFTLOD, _super);
                 function MSFTLOD() {
@@ -76804,49 +76820,71 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                MSFTLOD.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    // Clear out the extension so that it won't get loaded again.
-                    material.extensions[this.name] = undefined;
-                    // Tell the loader not to clear its state until the highest LOD is loaded.
-                    var materialLODs = [material.index].concat(properties.ids);
-                    loader.addLoaderPendingData(material);
-                    for (var index = 0; index < materialLODs.length; index++) {
-                        loader.addLoaderNonBlockingPendingData(index);
-                    }
-                    // Start with the lowest quality LOD.
-                    this.loadMaterialLOD(loader, material, materialLODs, materialLODs.length - 1, assign);
-                    return true;
+                MSFTLOD.prototype._traverseNode = function (loader, index, action, parentNode) {
+                    var node = loader._gltf.nodes[index];
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        for (var i = extension.ids.length - 1; i >= 0; i--) {
+                            loader._traverseNode(extension.ids[i], action, parentNode);
+                        }
+                        loader._traverseNode(index, action, parentNode);
+                        onComplete();
+                    });
                 };
-                MSFTLOD.prototype.loadMaterialLOD = function (loader, material, materialLODs, lod, assign) {
+                MSFTLOD.prototype._loadNode = function (loader, node) {
                     var _this = this;
-                    var materialLOD = loader.gltf.materials[materialLODs[lod]];
-                    if (lod !== materialLODs.length - 1) {
-                        loader.blockPendingTracking = true;
-                    }
-                    loader.loadMaterial(materialLOD, function (babylonMaterial, isNew) {
-                        assign(babylonMaterial, isNew);
-                        loader.removeLoaderPendingData(lod);
-                        // Loading is considered complete if this is the lowest quality LOD.
-                        if (lod === materialLODs.length - 1) {
-                            loader.removeLoaderPendingData(material);
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        var nodes = [node.index].concat(extension.ids).map(function (index) { return loader._gltf.nodes[index]; });
+                        loader._addLoaderPendingData(node);
+                        _this._loadNodeLOD(loader, nodes, nodes.length - 1, function () {
+                            loader._removeLoaderPendingData(node);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadNodeLOD = function (loader, nodes, index, onComplete) {
+                    var _this = this;
+                    loader._whenAction(function () {
+                        loader._loadNode(nodes[index]);
+                    }, function () {
+                        if (index !== nodes.length - 1) {
+                            var previousNode = nodes[index + 1];
+                            previousNode.babylonMesh.setEnabled(false);
                         }
-                        if (lod === 0) {
-                            loader.blockPendingTracking = false;
+                        if (index === 0) {
+                            onComplete();
+                            return;
+                        }
+                        setTimeout(function () {
+                            _this._loadNodeLOD(loader, nodes, index - 1, onComplete);
+                        }, MSFTLOD.MinimalLODDelay);
+                    });
+                };
+                MSFTLOD.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        var materials = [material.index].concat(extension.ids).map(function (index) { return loader._gltf.materials[index]; });
+                        loader._addLoaderPendingData(material);
+                        _this._loadMaterialLOD(loader, materials, materials.length - 1, assign, function () {
+                            material.extensions[_this.name] = extension;
+                            loader._removeLoaderPendingData(material);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadMaterialLOD = function (loader, materials, index, assign, onComplete) {
+                    var _this = this;
+                    loader._loadMaterial(materials[index], function (babylonMaterial, isNew) {
+                        assign(babylonMaterial, isNew);
+                        if (index === 0) {
+                            onComplete();
                             return;
                         }
                         // Load the next LOD when the loader is ready to render and
                         // all active material textures of the current LOD are loaded.
-                        loader.executeWhenRenderReady(function () {
+                        loader._executeWhenRenderReady(function () {
                             BABYLON.BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), function () {
                                 setTimeout(function () {
-                                    _this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
+                                    _this._loadMaterialLOD(loader, materials, index - 1, assign, onComplete);
                                 }, MSFTLOD.MinimalLODDelay);
                             });
                         });
@@ -76886,19 +76924,14 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                KHRMaterialsPbrSpecularGlossiness.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    loader.createPbrMaterial(material);
-                    loader.loadMaterialBaseProperties(material);
-                    this._loadSpecularGlossinessProperties(loader, material, properties);
-                    assign(material.babylonMaterial, true);
-                    return true;
+                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        loader._createPbrMaterial(material);
+                        loader._loadMaterialBaseProperties(material);
+                        _this._loadSpecularGlossinessProperties(loader, material, extension);
+                        assign(material.babylonMaterial, true);
+                    });
                 };
                 KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessProperties = function (loader, material, properties) {
                     var babylonMaterial = material.babylonMaterial;
@@ -76906,14 +76939,14 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : new BABYLON.Color3(1, 1, 1);
                     babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        babylonMaterial.albedoTexture = loader.loadTexture(properties.diffuseTexture);
+                        babylonMaterial.albedoTexture = loader._loadTexture(properties.diffuseTexture);
                     }
                     if (properties.specularGlossinessTexture) {
-                        babylonMaterial.reflectivityTexture = loader.loadTexture(properties.specularGlossinessTexture);
+                        babylonMaterial.reflectivityTexture = loader._loadTexture(properties.specularGlossinessTexture);
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader.loadMaterialAlphaProperties(material, properties.diffuseFactor);
+                    loader._loadMaterialAlphaProperties(material, properties.diffuseFactor);
                 };
                 return KHRMaterialsPbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4234 - 4234
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 263 - 263
dist/preview release/inspector/babylon.inspector.bundle.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 8 - 4
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -27,13 +27,17 @@ declare module BABYLON {
          */
         onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
         /**
-         * Raised when all LODs are complete (or if there is no LOD and model is complete)
+         * Raised when the visible components (geometry, materials, textures, etc.) are first ready to be rendered.
+         * For assets with LODs, raised when the first LOD is complete.
+         * For assets without LODs, raised when the model is complete just before onComplete.
          */
-        onComplete: () => void;
+        onReady: () => void;
         /**
-         * Raised when first LOD complete (or if there is no LOD and model is complete)
+         * Raised when the asset is completely loaded, just 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 just after onReady.
          */
-        onFirstLODComplete: () => void;
+        onComplete: () => void;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 37 - 33
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -27,13 +27,17 @@ declare module BABYLON {
          */
         onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
         /**
-         * Raised when all LODs are complete (or if there is no LOD and model is complete)
+         * Raised when the visible components (geometry, materials, textures, etc.) are first ready to be rendered.
+         * For assets with LODs, raised when the first LOD is complete.
+         * For assets without LODs, raised when the model is complete just before onComplete.
          */
-        onComplete: () => void;
+        onReady: () => void;
         /**
-         * Raised when first LOD complete (or if there is no LOD and model is complete)
+         * Raised when the asset is completely loaded, just 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 just after onReady.
          */
-        onFirstLODComplete: () => void;
+        onComplete: () => void;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -290,9 +294,9 @@ declare module BABYLON.GLTF2 {
 
 declare module BABYLON.GLTF2 {
     class GLTFLoader implements IGLTFLoader, IDisposable {
+        _gltf: IGLTF;
+        _babylonScene: Scene;
         private _parent;
-        private _gltf;
-        private _babylonScene;
         private _rootUrl;
         private _defaultMaterial;
         private _successCallback;
@@ -300,19 +304,14 @@ declare module BABYLON.GLTF2 {
         private _errorCallback;
         private _renderReady;
         private _disposed;
-        private _blockPendingTracking;
-        private _nonBlockingData;
-        private _rootMesh;
         private _renderReadyObservable;
         private _renderPendingCount;
         private _loaderPendingCount;
+        private _loaderTrackers;
         static Extensions: {
             [name: string]: GLTFLoaderExtension;
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
-        readonly gltf: IGLTF;
-        readonly babylonScene: Scene;
-        executeWhenRenderReady(func: () => void): void;
         constructor(parent: GLTFFileLoader);
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -320,18 +319,16 @@ declare module BABYLON.GLTF2 {
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess, onProgress, onError);
         private _onError(message);
         private _onProgress(event);
+        _executeWhenRenderReady(func: () => void): void;
         private _onRenderReady();
-        private _onLoaderComplete();
-        private _onLoaderFirstLODComplete();
+        private _onComplete();
         private _loadData(data);
-        private _addRightHandToLeftHandRootTransform();
         private _getMeshes();
         private _getSkeletons();
         private _getAnimationTargets();
-        private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
-        private _loadNode(node);
+        _loadNode(node: IGLTFNode): void;
         private _loadMesh(node, mesh);
         private _loadVertexDataAsync(primitive, onSuccess);
         private _createMorphTargets(node, mesh, primitive, babylonMesh);
@@ -343,7 +340,7 @@ declare module BABYLON.GLTF2 {
         private _loadBone(node, skin, inverseBindMatrixData, babylonBones);
         private _getNodeMatrix(node);
         private _traverseNodes(indices, action, parentNode?);
-        private _traverseNode(index, action, parentNode?);
+        _traverseNode(index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode?: IGLTFNode): void;
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
@@ -357,19 +354,18 @@ declare module BABYLON.GLTF2 {
         private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
-        blockPendingTracking: boolean;
-        addPendingData(data: any): void;
-        removePendingData(data: any): void;
-        addLoaderNonBlockingPendingData(data: any): void;
-        addLoaderPendingData(data: any): void;
-        removeLoaderPendingData(data: any): void;
+        _addPendingData(data: any): void;
+        _removePendingData(data: any): void;
+        _addLoaderPendingData(data: any): void;
+        _removeLoaderPendingData(data: any): void;
+        _whenAction(action: () => void, onComplete: () => void): void;
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessProperties(material);
-        loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
-        createPbrMaterial(material: IGLTFMaterial): void;
-        loadMaterialBaseProperties(material: IGLTFMaterial): void;
-        loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;
-        loadTexture(textureInfo: IGLTFTextureInfo): Texture;
+        _loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
+        _createPbrMaterial(material: IGLTFMaterial): void;
+        _loadMaterialBaseProperties(material: IGLTFMaterial): void;
+        _loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;
+        _loadTexture(textureInfo: IGLTFTextureInfo): Texture;
     }
 }
 
@@ -405,8 +401,13 @@ declare module BABYLON.GLTF2 {
     abstract class GLTFLoaderExtension {
         enabled: boolean;
         readonly abstract name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _loadExtension<T>(property: IGLTFProperty, action: (extension: T, onComplete: () => void) => void): boolean;
         static _Extensions: GLTFLoaderExtension[];
+        static TraverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        static LoadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
         static LoadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
         private static _ApplyExtensions(action);
     }
@@ -420,8 +421,11 @@ declare module BABYLON.GLTF2.Extensions {
          */
         static MinimalLODDelay: number;
         readonly name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
-        private loadMaterialLOD(loader, material, materialLODs, lod, assign);
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
+        private _loadNodeLOD(loader, nodes, index, onComplete);
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        private _loadMaterialLOD(loader, materials, index, assign, onComplete);
     }
 }
 
@@ -429,7 +433,7 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
         private _loadSpecularGlossinessProperties(loader, material, properties);
     }
 }

+ 234 - 207
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -320,17 +320,31 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        var GLTFLoaderTracker = (function () {
+            function GLTFLoaderTracker(onComplete) {
+                this._pendingCount = 0;
+                this._callback = onComplete;
+            }
+            GLTFLoaderTracker.prototype._addPendingData = function (data) {
+                this._pendingCount++;
+            };
+            GLTFLoaderTracker.prototype._removePendingData = function (data) {
+                if (--this._pendingCount === 0) {
+                    this._callback();
+                }
+            };
+            return GLTFLoaderTracker;
+        }());
         var GLTFLoader = (function () {
             function GLTFLoader(parent) {
                 this._renderReady = false;
                 this._disposed = false;
-                this._blockPendingTracking = false;
-                // Observable with boolean indicating success or error.
                 this._renderReadyObservable = new BABYLON.Observable();
                 // Count of pending work that needs to complete before the asset is rendered.
                 this._renderPendingCount = 0;
-                // Count of pending work that needs to complete before the loader is cleared.
+                // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
+                this._loaderTrackers = new Array();
                 this._parent = parent;
             }
             GLTFLoader.RegisterExtension = function (extension) {
@@ -342,28 +356,6 @@ var BABYLON;
                 // Keep the order of registration so that extensions registered first are called first.
                 GLTF2.GLTFLoaderExtension._Extensions.push(extension);
             };
-            Object.defineProperty(GLTFLoader.prototype, "gltf", {
-                get: function () {
-                    return this._gltf;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            Object.defineProperty(GLTFLoader.prototype, "babylonScene", {
-                get: function () {
-                    return this._babylonScene;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.executeWhenRenderReady = function (func) {
-                if (this._renderReady) {
-                    func();
-                }
-                else {
-                    this._renderReadyObservable.add(func);
-                }
-            };
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
@@ -404,10 +396,10 @@ var BABYLON;
                 this._successCallback = onSuccess;
                 this._progressCallback = onProgress;
                 this._errorCallback = onError;
-                this.addPendingData(this);
+                this._addPendingData(this);
                 this._loadScene(nodeNames);
                 this._loadAnimations();
-                this.removePendingData(this);
+                this._removePendingData(this);
             };
             GLTFLoader.prototype._onError = function (message) {
                 if (this._errorCallback) {
@@ -416,39 +408,33 @@ var BABYLON;
                 this.dispose();
             };
             GLTFLoader.prototype._onProgress = function (event) {
-                this._progressCallback(event);
+                if (this._progressCallback) {
+                    this._progressCallback(event);
+                }
             };
-            GLTFLoader.prototype._onRenderReady = function () {
-                switch (this._parent.coordinateSystemMode) {
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
-                        if (!this._babylonScene.useRightHandedSystem) {
-                            this._addRightHandToLeftHandRootTransform();
-                        }
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
-                        // do nothing
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
-                        this._babylonScene.useRightHandedSystem = true;
-                        break;
-                    default:
-                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
-                        break;
+            GLTFLoader.prototype._executeWhenRenderReady = function (func) {
+                if (this._renderReady) {
+                    func();
                 }
-                this._showMeshes();
+                else {
+                    this._renderReadyObservable.add(func);
+                }
+            };
+            GLTFLoader.prototype._onRenderReady = function () {
+                var rootNode = this._gltf.nodes[this._gltf.nodes.length - 1];
+                rootNode.babylonMesh.setEnabled(true);
                 this._startAnimations();
                 this._successCallback();
                 this._renderReadyObservable.notifyObservers(this);
+                if (this._parent.onReady) {
+                    this._parent.onReady();
+                }
             };
-            GLTFLoader.prototype._onLoaderComplete = function () {
+            GLTFLoader.prototype._onComplete = function () {
                 if (this._parent.onComplete) {
                     this._parent.onComplete();
                 }
-            };
-            GLTFLoader.prototype._onLoaderFirstLODComplete = function () {
-                if (this._parent.onFirstLODComplete) {
-                    this._parent.onFirstLODComplete();
-                }
+                this.dispose();
             };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
@@ -469,26 +455,8 @@ var BABYLON;
                     binaryBuffer.loadedData = data.bin;
                 }
             };
-            GLTFLoader.prototype._addRightHandToLeftHandRootTransform = function () {
-                this._rootMesh = new BABYLON.Mesh("root", this._babylonScene);
-                this._rootMesh.isVisible = false;
-                this._rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
-                this._rootMesh.rotation.y = Math.PI;
-                var nodes = this._gltf.nodes;
-                if (nodes) {
-                    for (var i = 0; i < nodes.length; i++) {
-                        var mesh = nodes[i].babylonMesh;
-                        if (mesh && !mesh.parent) {
-                            mesh.parent = this._rootMesh;
-                        }
-                    }
-                }
-            };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = [];
-                if (this._rootMesh) {
-                    meshes.push(this._rootMesh);
-                }
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     nodes.forEach(function (node) {
@@ -521,16 +489,34 @@ var BABYLON;
                 }
                 return targets;
             };
-            GLTFLoader.prototype._showMeshes = function () {
-                this._getMeshes().forEach(function (mesh) { return mesh.isVisible = true; });
-            };
             GLTFLoader.prototype._startAnimations = function () {
                 var _this = this;
                 this._getAnimationTargets().forEach(function (target) { return _this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true); });
             };
             GLTFLoader.prototype._loadScene = function (nodeNames) {
-                var _this = this;
                 var scene = this._gltf.scenes[this._gltf.scene || 0];
+                var rootNode = { name: "root", children: scene.nodes };
+                switch (this._parent.coordinateSystemMode) {
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
+                        if (!this._babylonScene.useRightHandedSystem) {
+                            rootNode.rotation = [0, 1, 0, 0];
+                            rootNode.scale = [1, 1, -1];
+                        }
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
+                        // do nothing
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
+                        this._babylonScene.useRightHandedSystem = true;
+                        break;
+                    default:
+                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
+                        break;
+                }
+                // Inject a root node into the scene.
+                this._gltf.nodes = this._gltf.nodes || [];
+                scene.nodes = [this._gltf.nodes.length];
+                this._gltf.nodes.push(rootNode);
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(nodeIndices, function (node, index, parentNode) {
                     node.index = index;
@@ -547,19 +533,25 @@ var BABYLON;
                     }
                     var filteredNodeIndices = new Array();
                     this._traverseNodes(nodeIndices, function (node) {
-                        if (nodeNames.indexOf(node.name) === -1) {
-                            return true;
+                        if (nodeNames.indexOf(node.name) !== -1) {
+                            filteredNodeIndices.push(node.index);
+                            return false;
                         }
-                        filteredNodeIndices.push(node.index);
-                        return false;
+                        return true;
                     });
                     nodeIndices = filteredNodeIndices;
                 }
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadNode(node); });
+                for (var i = 0; i < nodeIndices.length; i++) {
+                    this._loadNode(this._gltf.nodes[nodeIndices[i]]);
+                }
+                // Disable the root mesh until the asset is ready to render.
+                rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (node) {
+                if (GLTF2.GLTFLoaderExtension.LoadNode(this, node)) {
+                    return;
+                }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.isVisible = false;
                 this._loadTransform(node);
                 if (node.mesh !== undefined) {
                     var mesh = this._gltf.meshes[node.mesh];
@@ -576,7 +568,11 @@ var BABYLON;
                 if (node.camera !== undefined) {
                     // TODO: handle cameras
                 }
-                return true;
+                if (node.children) {
+                    for (var i = 0; i < node.children.length; i++) {
+                        this._loadNode(this._gltf.nodes[node.children[i]]);
+                    }
+                }
             };
             GLTFLoader.prototype._loadMesh = function (node, mesh) {
                 var _this = this;
@@ -608,25 +604,24 @@ var BABYLON;
                             loadMaterial: function () {
                                 if (primitive.material === undefined) {
                                     babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
+                                    return;
                                 }
-                                else {
-                                    var material = _this._gltf.materials[primitive.material];
-                                    _this.loadMaterial(material, function (babylonMaterial, isNew) {
-                                        if (isNew && _this._parent.onMaterialLoaded) {
-                                            _this._parent.onMaterialLoaded(babylonMaterial);
-                                        }
-                                        if (_this._parent.onBeforeMaterialReadyAsync) {
-                                            _this.addLoaderPendingData(material);
-                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
-                                                babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                                _this.removeLoaderPendingData(material);
-                                            });
-                                        }
-                                        else {
+                                var material = _this._gltf.materials[primitive.material];
+                                _this._loadMaterial(material, function (babylonMaterial, isNew) {
+                                    if (isNew && _this._parent.onMaterialLoaded) {
+                                        _this._parent.onMaterialLoaded(babylonMaterial);
+                                    }
+                                    if (_this._parent.onBeforeMaterialReadyAsync) {
+                                        _this._addLoaderPendingData(material);
+                                        _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
                                             babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                        }
-                                    });
-                                }
+                                            _this._removeLoaderPendingData(material);
+                                        });
+                                    }
+                                    else {
+                                        babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                    }
+                                });
                             }
                         });
                         vertexData.merge(subVertexData);
@@ -857,14 +852,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._traverseNode = function (index, action, parentNode) {
                 if (parentNode === void 0) { parentNode = null; }
+                if (GLTF2.GLTFLoaderExtension.TraverseNode(this, index, action, parentNode)) {
+                    return;
+                }
                 var node = this._gltf.nodes[index];
                 if (!action(node, index, parentNode)) {
                     return;
                 }
                 if (node.children) {
-                    for (var i = 0; i < node.children.length; i++) {
-                        this._traverseNode(node.children[i], action, node);
-                    }
+                    this._traverseNodes(node.children, action, node);
                 }
             };
             GLTFLoader.prototype._loadAnimations = function () {
@@ -992,11 +988,11 @@ var BABYLON;
             GLTFLoader.prototype._loadBufferAsync = function (index, onSuccess) {
                 var _this = this;
                 var buffer = this._gltf.buffers[index];
-                this.addPendingData(buffer);
+                this._addPendingData(buffer);
                 if (buffer.loadedData) {
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(buffer.uri)) {
@@ -1004,20 +1000,20 @@ var BABYLON;
                     buffer.loadedData = new Uint8Array(data);
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (buffer.loadedObservable) {
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else {
                     buffer.loadedObservable = new BABYLON.Observable();
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                     BABYLON.Tools.LoadFile(this._rootUrl + buffer.uri, function (data) {
                         buffer.loadedData = new Uint8Array(data);
@@ -1030,7 +1026,7 @@ var BABYLON;
                     }, this._babylonScene.database, true, function (request) {
                         if (!_this._disposed) {
                             _this._onError("Failed to load file '" + buffer.uri + "': " + request.status + " " + request.statusText);
-                            _this.removePendingData(buffer);
+                            _this._removePendingData(buffer);
                         }
                     });
                 }
@@ -1159,54 +1155,42 @@ var BABYLON;
                         return 0;
                 }
             };
-            Object.defineProperty(GLTFLoader.prototype, "blockPendingTracking", {
-                set: function (value) {
-                    this._blockPendingTracking = value;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.addPendingData = function (data) {
+            GLTFLoader.prototype._addPendingData = function (data) {
                 if (!this._renderReady) {
                     this._renderPendingCount++;
                 }
-                this.addLoaderPendingData(data);
+                this._addLoaderPendingData(data);
             };
-            GLTFLoader.prototype.removePendingData = function (data) {
+            GLTFLoader.prototype._removePendingData = function (data) {
                 if (!this._renderReady) {
                     if (--this._renderPendingCount === 0) {
                         this._renderReady = true;
                         this._onRenderReady();
                     }
                 }
-                this.removeLoaderPendingData(data);
-            };
-            GLTFLoader.prototype.addLoaderNonBlockingPendingData = function (data) {
-                if (!this._nonBlockingData) {
-                    this._nonBlockingData = new Array();
-                }
-                this._nonBlockingData.push(data);
+                this._removeLoaderPendingData(data);
             };
-            GLTFLoader.prototype.addLoaderPendingData = function (data) {
-                if (this._blockPendingTracking) {
-                    this.addLoaderNonBlockingPendingData(data);
-                    return;
-                }
+            GLTFLoader.prototype._addLoaderPendingData = function (data) {
                 this._loaderPendingCount++;
+                this._loaderTrackers.forEach(function (tracker) { return tracker._addPendingData(data); });
             };
-            GLTFLoader.prototype.removeLoaderPendingData = function (data) {
-                var indexInPending = this._nonBlockingData ? this._nonBlockingData.indexOf(data) : -1;
-                if (indexInPending !== -1) {
-                    this._nonBlockingData.splice(indexInPending, 1);
-                }
-                else if (--this._loaderPendingCount === 0) {
-                    this._onLoaderFirstLODComplete();
-                }
-                if ((!this._nonBlockingData || this._nonBlockingData.length === 0) && this._loaderPendingCount === 0) {
-                    this._onLoaderComplete();
-                    this.dispose();
+            GLTFLoader.prototype._removeLoaderPendingData = function (data) {
+                this._loaderTrackers.forEach(function (tracker) { return tracker._removePendingData(data); });
+                if (--this._loaderPendingCount === 0) {
+                    this._onComplete();
                 }
             };
+            GLTFLoader.prototype._whenAction = function (action, onComplete) {
+                var _this = this;
+                var tracker = new GLTFLoaderTracker(function () {
+                    _this._loaderTrackers.splice(_this._loaderTrackers.indexOf(tracker));
+                    onComplete();
+                });
+                this._loaderTrackers.push(tracker);
+                this._addLoaderPendingData(tracker);
+                action();
+                this._removeLoaderPendingData(tracker);
+            };
             GLTFLoader.prototype._getDefaultMaterial = function () {
                 if (!this._defaultMaterial) {
                     var id = "__gltf_default";
@@ -1234,17 +1218,17 @@ var BABYLON;
                 babylonMaterial.metallic = properties.metallicFactor === undefined ? 1 : properties.metallicFactor;
                 babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
                 if (properties.baseColorTexture) {
-                    babylonMaterial.albedoTexture = this.loadTexture(properties.baseColorTexture);
+                    babylonMaterial.albedoTexture = this._loadTexture(properties.baseColorTexture);
                 }
                 if (properties.metallicRoughnessTexture) {
-                    babylonMaterial.metallicTexture = this.loadTexture(properties.metallicRoughnessTexture);
+                    babylonMaterial.metallicTexture = this._loadTexture(properties.metallicRoughnessTexture);
                     babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
                     babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
                     babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
                 }
-                this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
+                this._loadMaterialAlphaProperties(material, properties.baseColorFactor);
             };
-            GLTFLoader.prototype.loadMaterial = function (material, assign) {
+            GLTFLoader.prototype._loadMaterial = function (material, assign) {
                 if (material.babylonMaterial) {
                     assign(material.babylonMaterial, false);
                     return;
@@ -1252,17 +1236,17 @@ var BABYLON;
                 if (GLTF2.GLTFLoaderExtension.LoadMaterial(this, material, assign)) {
                     return;
                 }
-                this.createPbrMaterial(material);
-                this.loadMaterialBaseProperties(material);
+                this._createPbrMaterial(material);
+                this._loadMaterialBaseProperties(material);
                 this._loadMaterialMetallicRoughnessProperties(material);
                 assign(material.babylonMaterial, true);
             };
-            GLTFLoader.prototype.createPbrMaterial = function (material) {
+            GLTFLoader.prototype._createPbrMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "mat" + material.index, this._babylonScene);
                 babylonMaterial.sideOrientation = BABYLON.Material.CounterClockWiseSideOrientation;
                 material.babylonMaterial = babylonMaterial;
             };
-            GLTFLoader.prototype.loadMaterialBaseProperties = function (material) {
+            GLTFLoader.prototype._loadMaterialBaseProperties = function (material) {
                 var babylonMaterial = material.babylonMaterial;
                 babylonMaterial.emissiveColor = material.emissiveFactor ? BABYLON.Color3.FromArray(material.emissiveFactor) : new BABYLON.Color3(0, 0, 0);
                 if (material.doubleSided) {
@@ -1270,7 +1254,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
+                    babylonMaterial.bumpTexture = this._loadTexture(material.normalTexture);
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
                     babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
                     if (material.normalTexture.scale !== undefined) {
@@ -1278,17 +1262,17 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    babylonMaterial.ambientTexture = this.loadTexture(material.occlusionTexture);
+                    babylonMaterial.ambientTexture = this._loadTexture(material.occlusionTexture);
                     babylonMaterial.useAmbientInGrayScale = true;
                     if (material.occlusionTexture.strength !== undefined) {
                         babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
                     }
                 }
                 if (material.emissiveTexture) {
-                    babylonMaterial.emissiveTexture = this.loadTexture(material.emissiveTexture);
+                    babylonMaterial.emissiveTexture = this._loadTexture(material.emissiveTexture);
                 }
             };
-            GLTFLoader.prototype.loadMaterialAlphaProperties = function (material, colorFactor) {
+            GLTFLoader.prototype._loadMaterialAlphaProperties = function (material, colorFactor) {
                 var babylonMaterial = material.babylonMaterial;
                 var alphaMode = material.alphaMode || "OPAQUE";
                 switch (alphaMode) {
@@ -1311,7 +1295,7 @@ var BABYLON;
                 }
                 babylonMaterial.alphaCutOff = material.alphaCutoff === undefined ? 0.5 : material.alphaCutoff;
             };
-            GLTFLoader.prototype.loadTexture = function (textureInfo) {
+            GLTFLoader.prototype._loadTexture = function (textureInfo) {
                 var _this = this;
                 var texture = this._gltf.textures[textureInfo.index];
                 var texCoord = textureInfo.texCoord || 0;
@@ -1322,15 +1306,15 @@ var BABYLON;
                 var sampler = (texture.sampler === undefined ? {} : this._gltf.samplers[texture.sampler]);
                 var noMipMaps = (sampler.minFilter === GLTF2.ETextureMinFilter.NEAREST || sampler.minFilter === GLTF2.ETextureMinFilter.LINEAR);
                 var samplingMode = GLTF2.GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
-                this.addPendingData(texture);
+                this._addPendingData(texture);
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, noMipMaps, false, samplingMode, function () {
                     if (!_this._disposed) {
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 }, function () {
                     if (!_this._disposed) {
                         _this._onError("Failed to load texture '" + source.uri + "'");
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 });
                 if (texture.url) {
@@ -1497,9 +1481,34 @@ var BABYLON;
             function GLTFLoaderExtension() {
                 this.enabled = true;
             }
-            GLTFLoaderExtension.prototype.loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._traverseNode = function (loader, index, action, parentNode) { return false; };
+            GLTFLoaderExtension.prototype._loadNode = function (loader, node) { return false; };
+            GLTFLoaderExtension.prototype._loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._loadExtension = function (property, action) {
+                var _this = this;
+                if (!property.extensions) {
+                    return false;
+                }
+                var extension = property.extensions[this.name];
+                if (!extension) {
+                    return false;
+                }
+                // Clear out the extension before executing the action to avoid recursing into the same property.
+                property.extensions[this.name] = undefined;
+                action(extension, function () {
+                    // Restore the extension after completing the action.
+                    property.extensions[_this.name] = extension;
+                });
+                return true;
+            };
+            GLTFLoaderExtension.TraverseNode = function (loader, index, action, parentNode) {
+                return this._ApplyExtensions(function (extension) { return extension._traverseNode(loader, index, action, parentNode); });
+            };
+            GLTFLoaderExtension.LoadNode = function (loader, node) {
+                return this._ApplyExtensions(function (extension) { return extension._loadNode(loader, node); });
+            };
             GLTFLoaderExtension.LoadMaterial = function (loader, material, assign) {
-                return this._ApplyExtensions(function (extension) { return extension.loadMaterial(loader, material, assign); });
+                return this._ApplyExtensions(function (extension) { return extension._loadMaterial(loader, material, assign); });
             };
             GLTFLoaderExtension._ApplyExtensions = function (action) {
                 var extensions = GLTFLoaderExtension._Extensions;
@@ -1543,6 +1552,7 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // See https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod for more information about this extension.
             var MSFTLOD = (function (_super) {
                 __extends(MSFTLOD, _super);
                 function MSFTLOD() {
@@ -1555,49 +1565,71 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                MSFTLOD.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    // Clear out the extension so that it won't get loaded again.
-                    material.extensions[this.name] = undefined;
-                    // Tell the loader not to clear its state until the highest LOD is loaded.
-                    var materialLODs = [material.index].concat(properties.ids);
-                    loader.addLoaderPendingData(material);
-                    for (var index = 0; index < materialLODs.length; index++) {
-                        loader.addLoaderNonBlockingPendingData(index);
-                    }
-                    // Start with the lowest quality LOD.
-                    this.loadMaterialLOD(loader, material, materialLODs, materialLODs.length - 1, assign);
-                    return true;
+                MSFTLOD.prototype._traverseNode = function (loader, index, action, parentNode) {
+                    var node = loader._gltf.nodes[index];
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        for (var i = extension.ids.length - 1; i >= 0; i--) {
+                            loader._traverseNode(extension.ids[i], action, parentNode);
+                        }
+                        loader._traverseNode(index, action, parentNode);
+                        onComplete();
+                    });
                 };
-                MSFTLOD.prototype.loadMaterialLOD = function (loader, material, materialLODs, lod, assign) {
+                MSFTLOD.prototype._loadNode = function (loader, node) {
                     var _this = this;
-                    var materialLOD = loader.gltf.materials[materialLODs[lod]];
-                    if (lod !== materialLODs.length - 1) {
-                        loader.blockPendingTracking = true;
-                    }
-                    loader.loadMaterial(materialLOD, function (babylonMaterial, isNew) {
-                        assign(babylonMaterial, isNew);
-                        loader.removeLoaderPendingData(lod);
-                        // Loading is considered complete if this is the lowest quality LOD.
-                        if (lod === materialLODs.length - 1) {
-                            loader.removeLoaderPendingData(material);
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        var nodes = [node.index].concat(extension.ids).map(function (index) { return loader._gltf.nodes[index]; });
+                        loader._addLoaderPendingData(node);
+                        _this._loadNodeLOD(loader, nodes, nodes.length - 1, function () {
+                            loader._removeLoaderPendingData(node);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadNodeLOD = function (loader, nodes, index, onComplete) {
+                    var _this = this;
+                    loader._whenAction(function () {
+                        loader._loadNode(nodes[index]);
+                    }, function () {
+                        if (index !== nodes.length - 1) {
+                            var previousNode = nodes[index + 1];
+                            previousNode.babylonMesh.setEnabled(false);
                         }
-                        if (lod === 0) {
-                            loader.blockPendingTracking = false;
+                        if (index === 0) {
+                            onComplete();
+                            return;
+                        }
+                        setTimeout(function () {
+                            _this._loadNodeLOD(loader, nodes, index - 1, onComplete);
+                        }, MSFTLOD.MinimalLODDelay);
+                    });
+                };
+                MSFTLOD.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        var materials = [material.index].concat(extension.ids).map(function (index) { return loader._gltf.materials[index]; });
+                        loader._addLoaderPendingData(material);
+                        _this._loadMaterialLOD(loader, materials, materials.length - 1, assign, function () {
+                            material.extensions[_this.name] = extension;
+                            loader._removeLoaderPendingData(material);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadMaterialLOD = function (loader, materials, index, assign, onComplete) {
+                    var _this = this;
+                    loader._loadMaterial(materials[index], function (babylonMaterial, isNew) {
+                        assign(babylonMaterial, isNew);
+                        if (index === 0) {
+                            onComplete();
                             return;
                         }
                         // Load the next LOD when the loader is ready to render and
                         // all active material textures of the current LOD are loaded.
-                        loader.executeWhenRenderReady(function () {
+                        loader._executeWhenRenderReady(function () {
                             BABYLON.BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), function () {
                                 setTimeout(function () {
-                                    _this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
+                                    _this._loadMaterialLOD(loader, materials, index - 1, assign, onComplete);
                                 }, MSFTLOD.MinimalLODDelay);
                             });
                         });
@@ -1646,19 +1678,14 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                KHRMaterialsPbrSpecularGlossiness.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    loader.createPbrMaterial(material);
-                    loader.loadMaterialBaseProperties(material);
-                    this._loadSpecularGlossinessProperties(loader, material, properties);
-                    assign(material.babylonMaterial, true);
-                    return true;
+                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        loader._createPbrMaterial(material);
+                        loader._loadMaterialBaseProperties(material);
+                        _this._loadSpecularGlossinessProperties(loader, material, extension);
+                        assign(material.babylonMaterial, true);
+                    });
                 };
                 KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessProperties = function (loader, material, properties) {
                     var babylonMaterial = material.babylonMaterial;
@@ -1666,14 +1693,14 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : new BABYLON.Color3(1, 1, 1);
                     babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        babylonMaterial.albedoTexture = loader.loadTexture(properties.diffuseTexture);
+                        babylonMaterial.albedoTexture = loader._loadTexture(properties.diffuseTexture);
                     }
                     if (properties.specularGlossinessTexture) {
-                        babylonMaterial.reflectivityTexture = loader.loadTexture(properties.specularGlossinessTexture);
+                        babylonMaterial.reflectivityTexture = loader._loadTexture(properties.specularGlossinessTexture);
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader.loadMaterialAlphaProperties(material, properties.diffuseFactor);
+                    loader._loadMaterialAlphaProperties(material, properties.diffuseFactor);
                 };
                 return KHRMaterialsPbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 37 - 33
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -27,13 +27,17 @@ declare module BABYLON {
          */
         onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
         /**
-         * Raised when all LODs are complete (or if there is no LOD and model is complete)
+         * Raised when the visible components (geometry, materials, textures, etc.) are first ready to be rendered.
+         * For assets with LODs, raised when the first LOD is complete.
+         * For assets without LODs, raised when the model is complete just before onComplete.
          */
-        onComplete: () => void;
+        onReady: () => void;
         /**
-         * Raised when first LOD complete (or if there is no LOD and model is complete)
+         * Raised when the asset is completely loaded, just 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 just after onReady.
          */
-        onFirstLODComplete: () => void;
+        onComplete: () => void;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -786,9 +790,9 @@ declare module BABYLON.GLTF2 {
 
 declare module BABYLON.GLTF2 {
     class GLTFLoader implements IGLTFLoader, IDisposable {
+        _gltf: IGLTF;
+        _babylonScene: Scene;
         private _parent;
-        private _gltf;
-        private _babylonScene;
         private _rootUrl;
         private _defaultMaterial;
         private _successCallback;
@@ -796,19 +800,14 @@ declare module BABYLON.GLTF2 {
         private _errorCallback;
         private _renderReady;
         private _disposed;
-        private _blockPendingTracking;
-        private _nonBlockingData;
-        private _rootMesh;
         private _renderReadyObservable;
         private _renderPendingCount;
         private _loaderPendingCount;
+        private _loaderTrackers;
         static Extensions: {
             [name: string]: GLTFLoaderExtension;
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
-        readonly gltf: IGLTF;
-        readonly babylonScene: Scene;
-        executeWhenRenderReady(func: () => void): void;
         constructor(parent: GLTFFileLoader);
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -816,18 +815,16 @@ declare module BABYLON.GLTF2 {
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess, onProgress, onError);
         private _onError(message);
         private _onProgress(event);
+        _executeWhenRenderReady(func: () => void): void;
         private _onRenderReady();
-        private _onLoaderComplete();
-        private _onLoaderFirstLODComplete();
+        private _onComplete();
         private _loadData(data);
-        private _addRightHandToLeftHandRootTransform();
         private _getMeshes();
         private _getSkeletons();
         private _getAnimationTargets();
-        private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
-        private _loadNode(node);
+        _loadNode(node: IGLTFNode): void;
         private _loadMesh(node, mesh);
         private _loadVertexDataAsync(primitive, onSuccess);
         private _createMorphTargets(node, mesh, primitive, babylonMesh);
@@ -839,7 +836,7 @@ declare module BABYLON.GLTF2 {
         private _loadBone(node, skin, inverseBindMatrixData, babylonBones);
         private _getNodeMatrix(node);
         private _traverseNodes(indices, action, parentNode?);
-        private _traverseNode(index, action, parentNode?);
+        _traverseNode(index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode?: IGLTFNode): void;
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
@@ -853,19 +850,18 @@ declare module BABYLON.GLTF2 {
         private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
-        blockPendingTracking: boolean;
-        addPendingData(data: any): void;
-        removePendingData(data: any): void;
-        addLoaderNonBlockingPendingData(data: any): void;
-        addLoaderPendingData(data: any): void;
-        removeLoaderPendingData(data: any): void;
+        _addPendingData(data: any): void;
+        _removePendingData(data: any): void;
+        _addLoaderPendingData(data: any): void;
+        _removeLoaderPendingData(data: any): void;
+        _whenAction(action: () => void, onComplete: () => void): void;
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessProperties(material);
-        loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
-        createPbrMaterial(material: IGLTFMaterial): void;
-        loadMaterialBaseProperties(material: IGLTFMaterial): void;
-        loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;
-        loadTexture(textureInfo: IGLTFTextureInfo): Texture;
+        _loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
+        _createPbrMaterial(material: IGLTFMaterial): void;
+        _loadMaterialBaseProperties(material: IGLTFMaterial): void;
+        _loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;
+        _loadTexture(textureInfo: IGLTFTextureInfo): Texture;
     }
 }
 
@@ -901,8 +897,13 @@ declare module BABYLON.GLTF2 {
     abstract class GLTFLoaderExtension {
         enabled: boolean;
         readonly abstract name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _loadExtension<T>(property: IGLTFProperty, action: (extension: T, onComplete: () => void) => void): boolean;
         static _Extensions: GLTFLoaderExtension[];
+        static TraverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        static LoadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
         static LoadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
         private static _ApplyExtensions(action);
     }
@@ -916,8 +917,11 @@ declare module BABYLON.GLTF2.Extensions {
          */
         static MinimalLODDelay: number;
         readonly name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
-        private loadMaterialLOD(loader, material, materialLODs, lod, assign);
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
+        private _loadNodeLOD(loader, nodes, index, onComplete);
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        private _loadMaterialLOD(loader, materials, index, assign, onComplete);
     }
 }
 
@@ -925,7 +929,7 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
         private _loadSpecularGlossinessProperties(loader, material, properties);
     }
 }

+ 234 - 207
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2478,17 +2478,31 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        var GLTFLoaderTracker = (function () {
+            function GLTFLoaderTracker(onComplete) {
+                this._pendingCount = 0;
+                this._callback = onComplete;
+            }
+            GLTFLoaderTracker.prototype._addPendingData = function (data) {
+                this._pendingCount++;
+            };
+            GLTFLoaderTracker.prototype._removePendingData = function (data) {
+                if (--this._pendingCount === 0) {
+                    this._callback();
+                }
+            };
+            return GLTFLoaderTracker;
+        }());
         var GLTFLoader = (function () {
             function GLTFLoader(parent) {
                 this._renderReady = false;
                 this._disposed = false;
-                this._blockPendingTracking = false;
-                // Observable with boolean indicating success or error.
                 this._renderReadyObservable = new BABYLON.Observable();
                 // Count of pending work that needs to complete before the asset is rendered.
                 this._renderPendingCount = 0;
-                // Count of pending work that needs to complete before the loader is cleared.
+                // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
+                this._loaderTrackers = new Array();
                 this._parent = parent;
             }
             GLTFLoader.RegisterExtension = function (extension) {
@@ -2500,28 +2514,6 @@ var BABYLON;
                 // Keep the order of registration so that extensions registered first are called first.
                 GLTF2.GLTFLoaderExtension._Extensions.push(extension);
             };
-            Object.defineProperty(GLTFLoader.prototype, "gltf", {
-                get: function () {
-                    return this._gltf;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            Object.defineProperty(GLTFLoader.prototype, "babylonScene", {
-                get: function () {
-                    return this._babylonScene;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.executeWhenRenderReady = function (func) {
-                if (this._renderReady) {
-                    func();
-                }
-                else {
-                    this._renderReadyObservable.add(func);
-                }
-            };
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
@@ -2562,10 +2554,10 @@ var BABYLON;
                 this._successCallback = onSuccess;
                 this._progressCallback = onProgress;
                 this._errorCallback = onError;
-                this.addPendingData(this);
+                this._addPendingData(this);
                 this._loadScene(nodeNames);
                 this._loadAnimations();
-                this.removePendingData(this);
+                this._removePendingData(this);
             };
             GLTFLoader.prototype._onError = function (message) {
                 if (this._errorCallback) {
@@ -2574,39 +2566,33 @@ var BABYLON;
                 this.dispose();
             };
             GLTFLoader.prototype._onProgress = function (event) {
-                this._progressCallback(event);
+                if (this._progressCallback) {
+                    this._progressCallback(event);
+                }
             };
-            GLTFLoader.prototype._onRenderReady = function () {
-                switch (this._parent.coordinateSystemMode) {
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
-                        if (!this._babylonScene.useRightHandedSystem) {
-                            this._addRightHandToLeftHandRootTransform();
-                        }
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
-                        // do nothing
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
-                        this._babylonScene.useRightHandedSystem = true;
-                        break;
-                    default:
-                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
-                        break;
+            GLTFLoader.prototype._executeWhenRenderReady = function (func) {
+                if (this._renderReady) {
+                    func();
                 }
-                this._showMeshes();
+                else {
+                    this._renderReadyObservable.add(func);
+                }
+            };
+            GLTFLoader.prototype._onRenderReady = function () {
+                var rootNode = this._gltf.nodes[this._gltf.nodes.length - 1];
+                rootNode.babylonMesh.setEnabled(true);
                 this._startAnimations();
                 this._successCallback();
                 this._renderReadyObservable.notifyObservers(this);
+                if (this._parent.onReady) {
+                    this._parent.onReady();
+                }
             };
-            GLTFLoader.prototype._onLoaderComplete = function () {
+            GLTFLoader.prototype._onComplete = function () {
                 if (this._parent.onComplete) {
                     this._parent.onComplete();
                 }
-            };
-            GLTFLoader.prototype._onLoaderFirstLODComplete = function () {
-                if (this._parent.onFirstLODComplete) {
-                    this._parent.onFirstLODComplete();
-                }
+                this.dispose();
             };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
@@ -2627,26 +2613,8 @@ var BABYLON;
                     binaryBuffer.loadedData = data.bin;
                 }
             };
-            GLTFLoader.prototype._addRightHandToLeftHandRootTransform = function () {
-                this._rootMesh = new BABYLON.Mesh("root", this._babylonScene);
-                this._rootMesh.isVisible = false;
-                this._rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
-                this._rootMesh.rotation.y = Math.PI;
-                var nodes = this._gltf.nodes;
-                if (nodes) {
-                    for (var i = 0; i < nodes.length; i++) {
-                        var mesh = nodes[i].babylonMesh;
-                        if (mesh && !mesh.parent) {
-                            mesh.parent = this._rootMesh;
-                        }
-                    }
-                }
-            };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = [];
-                if (this._rootMesh) {
-                    meshes.push(this._rootMesh);
-                }
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     nodes.forEach(function (node) {
@@ -2679,16 +2647,34 @@ var BABYLON;
                 }
                 return targets;
             };
-            GLTFLoader.prototype._showMeshes = function () {
-                this._getMeshes().forEach(function (mesh) { return mesh.isVisible = true; });
-            };
             GLTFLoader.prototype._startAnimations = function () {
                 var _this = this;
                 this._getAnimationTargets().forEach(function (target) { return _this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true); });
             };
             GLTFLoader.prototype._loadScene = function (nodeNames) {
-                var _this = this;
                 var scene = this._gltf.scenes[this._gltf.scene || 0];
+                var rootNode = { name: "root", children: scene.nodes };
+                switch (this._parent.coordinateSystemMode) {
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
+                        if (!this._babylonScene.useRightHandedSystem) {
+                            rootNode.rotation = [0, 1, 0, 0];
+                            rootNode.scale = [1, 1, -1];
+                        }
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
+                        // do nothing
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
+                        this._babylonScene.useRightHandedSystem = true;
+                        break;
+                    default:
+                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
+                        break;
+                }
+                // Inject a root node into the scene.
+                this._gltf.nodes = this._gltf.nodes || [];
+                scene.nodes = [this._gltf.nodes.length];
+                this._gltf.nodes.push(rootNode);
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(nodeIndices, function (node, index, parentNode) {
                     node.index = index;
@@ -2705,19 +2691,25 @@ var BABYLON;
                     }
                     var filteredNodeIndices = new Array();
                     this._traverseNodes(nodeIndices, function (node) {
-                        if (nodeNames.indexOf(node.name) === -1) {
-                            return true;
+                        if (nodeNames.indexOf(node.name) !== -1) {
+                            filteredNodeIndices.push(node.index);
+                            return false;
                         }
-                        filteredNodeIndices.push(node.index);
-                        return false;
+                        return true;
                     });
                     nodeIndices = filteredNodeIndices;
                 }
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadNode(node); });
+                for (var i = 0; i < nodeIndices.length; i++) {
+                    this._loadNode(this._gltf.nodes[nodeIndices[i]]);
+                }
+                // Disable the root mesh until the asset is ready to render.
+                rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (node) {
+                if (GLTF2.GLTFLoaderExtension.LoadNode(this, node)) {
+                    return;
+                }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.isVisible = false;
                 this._loadTransform(node);
                 if (node.mesh !== undefined) {
                     var mesh = this._gltf.meshes[node.mesh];
@@ -2734,7 +2726,11 @@ var BABYLON;
                 if (node.camera !== undefined) {
                     // TODO: handle cameras
                 }
-                return true;
+                if (node.children) {
+                    for (var i = 0; i < node.children.length; i++) {
+                        this._loadNode(this._gltf.nodes[node.children[i]]);
+                    }
+                }
             };
             GLTFLoader.prototype._loadMesh = function (node, mesh) {
                 var _this = this;
@@ -2766,25 +2762,24 @@ var BABYLON;
                             loadMaterial: function () {
                                 if (primitive.material === undefined) {
                                     babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
+                                    return;
                                 }
-                                else {
-                                    var material = _this._gltf.materials[primitive.material];
-                                    _this.loadMaterial(material, function (babylonMaterial, isNew) {
-                                        if (isNew && _this._parent.onMaterialLoaded) {
-                                            _this._parent.onMaterialLoaded(babylonMaterial);
-                                        }
-                                        if (_this._parent.onBeforeMaterialReadyAsync) {
-                                            _this.addLoaderPendingData(material);
-                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
-                                                babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                                _this.removeLoaderPendingData(material);
-                                            });
-                                        }
-                                        else {
+                                var material = _this._gltf.materials[primitive.material];
+                                _this._loadMaterial(material, function (babylonMaterial, isNew) {
+                                    if (isNew && _this._parent.onMaterialLoaded) {
+                                        _this._parent.onMaterialLoaded(babylonMaterial);
+                                    }
+                                    if (_this._parent.onBeforeMaterialReadyAsync) {
+                                        _this._addLoaderPendingData(material);
+                                        _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
                                             babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                        }
-                                    });
-                                }
+                                            _this._removeLoaderPendingData(material);
+                                        });
+                                    }
+                                    else {
+                                        babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                    }
+                                });
                             }
                         });
                         vertexData.merge(subVertexData);
@@ -3015,14 +3010,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._traverseNode = function (index, action, parentNode) {
                 if (parentNode === void 0) { parentNode = null; }
+                if (GLTF2.GLTFLoaderExtension.TraverseNode(this, index, action, parentNode)) {
+                    return;
+                }
                 var node = this._gltf.nodes[index];
                 if (!action(node, index, parentNode)) {
                     return;
                 }
                 if (node.children) {
-                    for (var i = 0; i < node.children.length; i++) {
-                        this._traverseNode(node.children[i], action, node);
-                    }
+                    this._traverseNodes(node.children, action, node);
                 }
             };
             GLTFLoader.prototype._loadAnimations = function () {
@@ -3150,11 +3146,11 @@ var BABYLON;
             GLTFLoader.prototype._loadBufferAsync = function (index, onSuccess) {
                 var _this = this;
                 var buffer = this._gltf.buffers[index];
-                this.addPendingData(buffer);
+                this._addPendingData(buffer);
                 if (buffer.loadedData) {
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(buffer.uri)) {
@@ -3162,20 +3158,20 @@ var BABYLON;
                     buffer.loadedData = new Uint8Array(data);
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (buffer.loadedObservable) {
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else {
                     buffer.loadedObservable = new BABYLON.Observable();
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                     BABYLON.Tools.LoadFile(this._rootUrl + buffer.uri, function (data) {
                         buffer.loadedData = new Uint8Array(data);
@@ -3188,7 +3184,7 @@ var BABYLON;
                     }, this._babylonScene.database, true, function (request) {
                         if (!_this._disposed) {
                             _this._onError("Failed to load file '" + buffer.uri + "': " + request.status + " " + request.statusText);
-                            _this.removePendingData(buffer);
+                            _this._removePendingData(buffer);
                         }
                     });
                 }
@@ -3317,54 +3313,42 @@ var BABYLON;
                         return 0;
                 }
             };
-            Object.defineProperty(GLTFLoader.prototype, "blockPendingTracking", {
-                set: function (value) {
-                    this._blockPendingTracking = value;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.addPendingData = function (data) {
+            GLTFLoader.prototype._addPendingData = function (data) {
                 if (!this._renderReady) {
                     this._renderPendingCount++;
                 }
-                this.addLoaderPendingData(data);
+                this._addLoaderPendingData(data);
             };
-            GLTFLoader.prototype.removePendingData = function (data) {
+            GLTFLoader.prototype._removePendingData = function (data) {
                 if (!this._renderReady) {
                     if (--this._renderPendingCount === 0) {
                         this._renderReady = true;
                         this._onRenderReady();
                     }
                 }
-                this.removeLoaderPendingData(data);
-            };
-            GLTFLoader.prototype.addLoaderNonBlockingPendingData = function (data) {
-                if (!this._nonBlockingData) {
-                    this._nonBlockingData = new Array();
-                }
-                this._nonBlockingData.push(data);
+                this._removeLoaderPendingData(data);
             };
-            GLTFLoader.prototype.addLoaderPendingData = function (data) {
-                if (this._blockPendingTracking) {
-                    this.addLoaderNonBlockingPendingData(data);
-                    return;
-                }
+            GLTFLoader.prototype._addLoaderPendingData = function (data) {
                 this._loaderPendingCount++;
+                this._loaderTrackers.forEach(function (tracker) { return tracker._addPendingData(data); });
             };
-            GLTFLoader.prototype.removeLoaderPendingData = function (data) {
-                var indexInPending = this._nonBlockingData ? this._nonBlockingData.indexOf(data) : -1;
-                if (indexInPending !== -1) {
-                    this._nonBlockingData.splice(indexInPending, 1);
-                }
-                else if (--this._loaderPendingCount === 0) {
-                    this._onLoaderFirstLODComplete();
-                }
-                if ((!this._nonBlockingData || this._nonBlockingData.length === 0) && this._loaderPendingCount === 0) {
-                    this._onLoaderComplete();
-                    this.dispose();
+            GLTFLoader.prototype._removeLoaderPendingData = function (data) {
+                this._loaderTrackers.forEach(function (tracker) { return tracker._removePendingData(data); });
+                if (--this._loaderPendingCount === 0) {
+                    this._onComplete();
                 }
             };
+            GLTFLoader.prototype._whenAction = function (action, onComplete) {
+                var _this = this;
+                var tracker = new GLTFLoaderTracker(function () {
+                    _this._loaderTrackers.splice(_this._loaderTrackers.indexOf(tracker));
+                    onComplete();
+                });
+                this._loaderTrackers.push(tracker);
+                this._addLoaderPendingData(tracker);
+                action();
+                this._removeLoaderPendingData(tracker);
+            };
             GLTFLoader.prototype._getDefaultMaterial = function () {
                 if (!this._defaultMaterial) {
                     var id = "__gltf_default";
@@ -3392,17 +3376,17 @@ var BABYLON;
                 babylonMaterial.metallic = properties.metallicFactor === undefined ? 1 : properties.metallicFactor;
                 babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
                 if (properties.baseColorTexture) {
-                    babylonMaterial.albedoTexture = this.loadTexture(properties.baseColorTexture);
+                    babylonMaterial.albedoTexture = this._loadTexture(properties.baseColorTexture);
                 }
                 if (properties.metallicRoughnessTexture) {
-                    babylonMaterial.metallicTexture = this.loadTexture(properties.metallicRoughnessTexture);
+                    babylonMaterial.metallicTexture = this._loadTexture(properties.metallicRoughnessTexture);
                     babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
                     babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
                     babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
                 }
-                this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
+                this._loadMaterialAlphaProperties(material, properties.baseColorFactor);
             };
-            GLTFLoader.prototype.loadMaterial = function (material, assign) {
+            GLTFLoader.prototype._loadMaterial = function (material, assign) {
                 if (material.babylonMaterial) {
                     assign(material.babylonMaterial, false);
                     return;
@@ -3410,17 +3394,17 @@ var BABYLON;
                 if (GLTF2.GLTFLoaderExtension.LoadMaterial(this, material, assign)) {
                     return;
                 }
-                this.createPbrMaterial(material);
-                this.loadMaterialBaseProperties(material);
+                this._createPbrMaterial(material);
+                this._loadMaterialBaseProperties(material);
                 this._loadMaterialMetallicRoughnessProperties(material);
                 assign(material.babylonMaterial, true);
             };
-            GLTFLoader.prototype.createPbrMaterial = function (material) {
+            GLTFLoader.prototype._createPbrMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "mat" + material.index, this._babylonScene);
                 babylonMaterial.sideOrientation = BABYLON.Material.CounterClockWiseSideOrientation;
                 material.babylonMaterial = babylonMaterial;
             };
-            GLTFLoader.prototype.loadMaterialBaseProperties = function (material) {
+            GLTFLoader.prototype._loadMaterialBaseProperties = function (material) {
                 var babylonMaterial = material.babylonMaterial;
                 babylonMaterial.emissiveColor = material.emissiveFactor ? BABYLON.Color3.FromArray(material.emissiveFactor) : new BABYLON.Color3(0, 0, 0);
                 if (material.doubleSided) {
@@ -3428,7 +3412,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
+                    babylonMaterial.bumpTexture = this._loadTexture(material.normalTexture);
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
                     babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
                     if (material.normalTexture.scale !== undefined) {
@@ -3436,17 +3420,17 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    babylonMaterial.ambientTexture = this.loadTexture(material.occlusionTexture);
+                    babylonMaterial.ambientTexture = this._loadTexture(material.occlusionTexture);
                     babylonMaterial.useAmbientInGrayScale = true;
                     if (material.occlusionTexture.strength !== undefined) {
                         babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
                     }
                 }
                 if (material.emissiveTexture) {
-                    babylonMaterial.emissiveTexture = this.loadTexture(material.emissiveTexture);
+                    babylonMaterial.emissiveTexture = this._loadTexture(material.emissiveTexture);
                 }
             };
-            GLTFLoader.prototype.loadMaterialAlphaProperties = function (material, colorFactor) {
+            GLTFLoader.prototype._loadMaterialAlphaProperties = function (material, colorFactor) {
                 var babylonMaterial = material.babylonMaterial;
                 var alphaMode = material.alphaMode || "OPAQUE";
                 switch (alphaMode) {
@@ -3469,7 +3453,7 @@ var BABYLON;
                 }
                 babylonMaterial.alphaCutOff = material.alphaCutoff === undefined ? 0.5 : material.alphaCutoff;
             };
-            GLTFLoader.prototype.loadTexture = function (textureInfo) {
+            GLTFLoader.prototype._loadTexture = function (textureInfo) {
                 var _this = this;
                 var texture = this._gltf.textures[textureInfo.index];
                 var texCoord = textureInfo.texCoord || 0;
@@ -3480,15 +3464,15 @@ var BABYLON;
                 var sampler = (texture.sampler === undefined ? {} : this._gltf.samplers[texture.sampler]);
                 var noMipMaps = (sampler.minFilter === GLTF2.ETextureMinFilter.NEAREST || sampler.minFilter === GLTF2.ETextureMinFilter.LINEAR);
                 var samplingMode = GLTF2.GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
-                this.addPendingData(texture);
+                this._addPendingData(texture);
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, noMipMaps, false, samplingMode, function () {
                     if (!_this._disposed) {
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 }, function () {
                     if (!_this._disposed) {
                         _this._onError("Failed to load texture '" + source.uri + "'");
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 });
                 if (texture.url) {
@@ -3655,9 +3639,34 @@ var BABYLON;
             function GLTFLoaderExtension() {
                 this.enabled = true;
             }
-            GLTFLoaderExtension.prototype.loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._traverseNode = function (loader, index, action, parentNode) { return false; };
+            GLTFLoaderExtension.prototype._loadNode = function (loader, node) { return false; };
+            GLTFLoaderExtension.prototype._loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._loadExtension = function (property, action) {
+                var _this = this;
+                if (!property.extensions) {
+                    return false;
+                }
+                var extension = property.extensions[this.name];
+                if (!extension) {
+                    return false;
+                }
+                // Clear out the extension before executing the action to avoid recursing into the same property.
+                property.extensions[this.name] = undefined;
+                action(extension, function () {
+                    // Restore the extension after completing the action.
+                    property.extensions[_this.name] = extension;
+                });
+                return true;
+            };
+            GLTFLoaderExtension.TraverseNode = function (loader, index, action, parentNode) {
+                return this._ApplyExtensions(function (extension) { return extension._traverseNode(loader, index, action, parentNode); });
+            };
+            GLTFLoaderExtension.LoadNode = function (loader, node) {
+                return this._ApplyExtensions(function (extension) { return extension._loadNode(loader, node); });
+            };
             GLTFLoaderExtension.LoadMaterial = function (loader, material, assign) {
-                return this._ApplyExtensions(function (extension) { return extension.loadMaterial(loader, material, assign); });
+                return this._ApplyExtensions(function (extension) { return extension._loadMaterial(loader, material, assign); });
             };
             GLTFLoaderExtension._ApplyExtensions = function (action) {
                 var extensions = GLTFLoaderExtension._Extensions;
@@ -3701,6 +3710,7 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // See https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod for more information about this extension.
             var MSFTLOD = (function (_super) {
                 __extends(MSFTLOD, _super);
                 function MSFTLOD() {
@@ -3713,49 +3723,71 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                MSFTLOD.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    // Clear out the extension so that it won't get loaded again.
-                    material.extensions[this.name] = undefined;
-                    // Tell the loader not to clear its state until the highest LOD is loaded.
-                    var materialLODs = [material.index].concat(properties.ids);
-                    loader.addLoaderPendingData(material);
-                    for (var index = 0; index < materialLODs.length; index++) {
-                        loader.addLoaderNonBlockingPendingData(index);
-                    }
-                    // Start with the lowest quality LOD.
-                    this.loadMaterialLOD(loader, material, materialLODs, materialLODs.length - 1, assign);
-                    return true;
+                MSFTLOD.prototype._traverseNode = function (loader, index, action, parentNode) {
+                    var node = loader._gltf.nodes[index];
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        for (var i = extension.ids.length - 1; i >= 0; i--) {
+                            loader._traverseNode(extension.ids[i], action, parentNode);
+                        }
+                        loader._traverseNode(index, action, parentNode);
+                        onComplete();
+                    });
                 };
-                MSFTLOD.prototype.loadMaterialLOD = function (loader, material, materialLODs, lod, assign) {
+                MSFTLOD.prototype._loadNode = function (loader, node) {
                     var _this = this;
-                    var materialLOD = loader.gltf.materials[materialLODs[lod]];
-                    if (lod !== materialLODs.length - 1) {
-                        loader.blockPendingTracking = true;
-                    }
-                    loader.loadMaterial(materialLOD, function (babylonMaterial, isNew) {
-                        assign(babylonMaterial, isNew);
-                        loader.removeLoaderPendingData(lod);
-                        // Loading is considered complete if this is the lowest quality LOD.
-                        if (lod === materialLODs.length - 1) {
-                            loader.removeLoaderPendingData(material);
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        var nodes = [node.index].concat(extension.ids).map(function (index) { return loader._gltf.nodes[index]; });
+                        loader._addLoaderPendingData(node);
+                        _this._loadNodeLOD(loader, nodes, nodes.length - 1, function () {
+                            loader._removeLoaderPendingData(node);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadNodeLOD = function (loader, nodes, index, onComplete) {
+                    var _this = this;
+                    loader._whenAction(function () {
+                        loader._loadNode(nodes[index]);
+                    }, function () {
+                        if (index !== nodes.length - 1) {
+                            var previousNode = nodes[index + 1];
+                            previousNode.babylonMesh.setEnabled(false);
                         }
-                        if (lod === 0) {
-                            loader.blockPendingTracking = false;
+                        if (index === 0) {
+                            onComplete();
+                            return;
+                        }
+                        setTimeout(function () {
+                            _this._loadNodeLOD(loader, nodes, index - 1, onComplete);
+                        }, MSFTLOD.MinimalLODDelay);
+                    });
+                };
+                MSFTLOD.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        var materials = [material.index].concat(extension.ids).map(function (index) { return loader._gltf.materials[index]; });
+                        loader._addLoaderPendingData(material);
+                        _this._loadMaterialLOD(loader, materials, materials.length - 1, assign, function () {
+                            material.extensions[_this.name] = extension;
+                            loader._removeLoaderPendingData(material);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadMaterialLOD = function (loader, materials, index, assign, onComplete) {
+                    var _this = this;
+                    loader._loadMaterial(materials[index], function (babylonMaterial, isNew) {
+                        assign(babylonMaterial, isNew);
+                        if (index === 0) {
+                            onComplete();
                             return;
                         }
                         // Load the next LOD when the loader is ready to render and
                         // all active material textures of the current LOD are loaded.
-                        loader.executeWhenRenderReady(function () {
+                        loader._executeWhenRenderReady(function () {
                             BABYLON.BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), function () {
                                 setTimeout(function () {
-                                    _this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
+                                    _this._loadMaterialLOD(loader, materials, index - 1, assign, onComplete);
                                 }, MSFTLOD.MinimalLODDelay);
                             });
                         });
@@ -3804,19 +3836,14 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                KHRMaterialsPbrSpecularGlossiness.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    loader.createPbrMaterial(material);
-                    loader.loadMaterialBaseProperties(material);
-                    this._loadSpecularGlossinessProperties(loader, material, properties);
-                    assign(material.babylonMaterial, true);
-                    return true;
+                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        loader._createPbrMaterial(material);
+                        loader._loadMaterialBaseProperties(material);
+                        _this._loadSpecularGlossinessProperties(loader, material, extension);
+                        assign(material.babylonMaterial, true);
+                    });
                 };
                 KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessProperties = function (loader, material, properties) {
                     var babylonMaterial = material.babylonMaterial;
@@ -3824,14 +3851,14 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : new BABYLON.Color3(1, 1, 1);
                     babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        babylonMaterial.albedoTexture = loader.loadTexture(properties.diffuseTexture);
+                        babylonMaterial.albedoTexture = loader._loadTexture(properties.diffuseTexture);
                     }
                     if (properties.specularGlossinessTexture) {
-                        babylonMaterial.reflectivityTexture = loader.loadTexture(properties.specularGlossinessTexture);
+                        babylonMaterial.reflectivityTexture = loader._loadTexture(properties.specularGlossinessTexture);
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader.loadMaterialAlphaProperties(material, properties.diffuseFactor);
+                    loader._loadMaterialAlphaProperties(material, properties.diffuseFactor);
                 };
                 return KHRMaterialsPbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 234 - 207
dist/preview release/loaders/babylonjs.loaders.js

@@ -3430,17 +3430,31 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        var GLTFLoaderTracker = (function () {
+            function GLTFLoaderTracker(onComplete) {
+                this._pendingCount = 0;
+                this._callback = onComplete;
+            }
+            GLTFLoaderTracker.prototype._addPendingData = function (data) {
+                this._pendingCount++;
+            };
+            GLTFLoaderTracker.prototype._removePendingData = function (data) {
+                if (--this._pendingCount === 0) {
+                    this._callback();
+                }
+            };
+            return GLTFLoaderTracker;
+        }());
         var GLTFLoader = (function () {
             function GLTFLoader(parent) {
                 this._renderReady = false;
                 this._disposed = false;
-                this._blockPendingTracking = false;
-                // Observable with boolean indicating success or error.
                 this._renderReadyObservable = new BABYLON.Observable();
                 // Count of pending work that needs to complete before the asset is rendered.
                 this._renderPendingCount = 0;
-                // Count of pending work that needs to complete before the loader is cleared.
+                // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
+                this._loaderTrackers = new Array();
                 this._parent = parent;
             }
             GLTFLoader.RegisterExtension = function (extension) {
@@ -3452,28 +3466,6 @@ var BABYLON;
                 // Keep the order of registration so that extensions registered first are called first.
                 GLTF2.GLTFLoaderExtension._Extensions.push(extension);
             };
-            Object.defineProperty(GLTFLoader.prototype, "gltf", {
-                get: function () {
-                    return this._gltf;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            Object.defineProperty(GLTFLoader.prototype, "babylonScene", {
-                get: function () {
-                    return this._babylonScene;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.executeWhenRenderReady = function (func) {
-                if (this._renderReady) {
-                    func();
-                }
-                else {
-                    this._renderReadyObservable.add(func);
-                }
-            };
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
@@ -3514,10 +3506,10 @@ var BABYLON;
                 this._successCallback = onSuccess;
                 this._progressCallback = onProgress;
                 this._errorCallback = onError;
-                this.addPendingData(this);
+                this._addPendingData(this);
                 this._loadScene(nodeNames);
                 this._loadAnimations();
-                this.removePendingData(this);
+                this._removePendingData(this);
             };
             GLTFLoader.prototype._onError = function (message) {
                 if (this._errorCallback) {
@@ -3526,39 +3518,33 @@ var BABYLON;
                 this.dispose();
             };
             GLTFLoader.prototype._onProgress = function (event) {
-                this._progressCallback(event);
+                if (this._progressCallback) {
+                    this._progressCallback(event);
+                }
             };
-            GLTFLoader.prototype._onRenderReady = function () {
-                switch (this._parent.coordinateSystemMode) {
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
-                        if (!this._babylonScene.useRightHandedSystem) {
-                            this._addRightHandToLeftHandRootTransform();
-                        }
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
-                        // do nothing
-                        break;
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
-                        this._babylonScene.useRightHandedSystem = true;
-                        break;
-                    default:
-                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
-                        break;
+            GLTFLoader.prototype._executeWhenRenderReady = function (func) {
+                if (this._renderReady) {
+                    func();
                 }
-                this._showMeshes();
+                else {
+                    this._renderReadyObservable.add(func);
+                }
+            };
+            GLTFLoader.prototype._onRenderReady = function () {
+                var rootNode = this._gltf.nodes[this._gltf.nodes.length - 1];
+                rootNode.babylonMesh.setEnabled(true);
                 this._startAnimations();
                 this._successCallback();
                 this._renderReadyObservable.notifyObservers(this);
+                if (this._parent.onReady) {
+                    this._parent.onReady();
+                }
             };
-            GLTFLoader.prototype._onLoaderComplete = function () {
+            GLTFLoader.prototype._onComplete = function () {
                 if (this._parent.onComplete) {
                     this._parent.onComplete();
                 }
-            };
-            GLTFLoader.prototype._onLoaderFirstLODComplete = function () {
-                if (this._parent.onFirstLODComplete) {
-                    this._parent.onFirstLODComplete();
-                }
+                this.dispose();
             };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
@@ -3579,26 +3565,8 @@ var BABYLON;
                     binaryBuffer.loadedData = data.bin;
                 }
             };
-            GLTFLoader.prototype._addRightHandToLeftHandRootTransform = function () {
-                this._rootMesh = new BABYLON.Mesh("root", this._babylonScene);
-                this._rootMesh.isVisible = false;
-                this._rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
-                this._rootMesh.rotation.y = Math.PI;
-                var nodes = this._gltf.nodes;
-                if (nodes) {
-                    for (var i = 0; i < nodes.length; i++) {
-                        var mesh = nodes[i].babylonMesh;
-                        if (mesh && !mesh.parent) {
-                            mesh.parent = this._rootMesh;
-                        }
-                    }
-                }
-            };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = [];
-                if (this._rootMesh) {
-                    meshes.push(this._rootMesh);
-                }
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     nodes.forEach(function (node) {
@@ -3631,16 +3599,34 @@ var BABYLON;
                 }
                 return targets;
             };
-            GLTFLoader.prototype._showMeshes = function () {
-                this._getMeshes().forEach(function (mesh) { return mesh.isVisible = true; });
-            };
             GLTFLoader.prototype._startAnimations = function () {
                 var _this = this;
                 this._getAnimationTargets().forEach(function (target) { return _this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true); });
             };
             GLTFLoader.prototype._loadScene = function (nodeNames) {
-                var _this = this;
                 var scene = this._gltf.scenes[this._gltf.scene || 0];
+                var rootNode = { name: "root", children: scene.nodes };
+                switch (this._parent.coordinateSystemMode) {
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO:
+                        if (!this._babylonScene.useRightHandedSystem) {
+                            rootNode.rotation = [0, 1, 0, 0];
+                            rootNode.scale = [1, 1, -1];
+                        }
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
+                        // do nothing
+                        break;
+                    case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
+                        this._babylonScene.useRightHandedSystem = true;
+                        break;
+                    default:
+                        BABYLON.Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
+                        break;
+                }
+                // Inject a root node into the scene.
+                this._gltf.nodes = this._gltf.nodes || [];
+                scene.nodes = [this._gltf.nodes.length];
+                this._gltf.nodes.push(rootNode);
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(nodeIndices, function (node, index, parentNode) {
                     node.index = index;
@@ -3657,19 +3643,25 @@ var BABYLON;
                     }
                     var filteredNodeIndices = new Array();
                     this._traverseNodes(nodeIndices, function (node) {
-                        if (nodeNames.indexOf(node.name) === -1) {
-                            return true;
+                        if (nodeNames.indexOf(node.name) !== -1) {
+                            filteredNodeIndices.push(node.index);
+                            return false;
                         }
-                        filteredNodeIndices.push(node.index);
-                        return false;
+                        return true;
                     });
                     nodeIndices = filteredNodeIndices;
                 }
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadNode(node); });
+                for (var i = 0; i < nodeIndices.length; i++) {
+                    this._loadNode(this._gltf.nodes[nodeIndices[i]]);
+                }
+                // Disable the root mesh until the asset is ready to render.
+                rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (node) {
+                if (GLTF2.GLTFLoaderExtension.LoadNode(this, node)) {
+                    return;
+                }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.isVisible = false;
                 this._loadTransform(node);
                 if (node.mesh !== undefined) {
                     var mesh = this._gltf.meshes[node.mesh];
@@ -3686,7 +3678,11 @@ var BABYLON;
                 if (node.camera !== undefined) {
                     // TODO: handle cameras
                 }
-                return true;
+                if (node.children) {
+                    for (var i = 0; i < node.children.length; i++) {
+                        this._loadNode(this._gltf.nodes[node.children[i]]);
+                    }
+                }
             };
             GLTFLoader.prototype._loadMesh = function (node, mesh) {
                 var _this = this;
@@ -3718,25 +3714,24 @@ var BABYLON;
                             loadMaterial: function () {
                                 if (primitive.material === undefined) {
                                     babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
+                                    return;
                                 }
-                                else {
-                                    var material = _this._gltf.materials[primitive.material];
-                                    _this.loadMaterial(material, function (babylonMaterial, isNew) {
-                                        if (isNew && _this._parent.onMaterialLoaded) {
-                                            _this._parent.onMaterialLoaded(babylonMaterial);
-                                        }
-                                        if (_this._parent.onBeforeMaterialReadyAsync) {
-                                            _this.addLoaderPendingData(material);
-                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
-                                                babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                                _this.removeLoaderPendingData(material);
-                                            });
-                                        }
-                                        else {
+                                var material = _this._gltf.materials[primitive.material];
+                                _this._loadMaterial(material, function (babylonMaterial, isNew) {
+                                    if (isNew && _this._parent.onMaterialLoaded) {
+                                        _this._parent.onMaterialLoaded(babylonMaterial);
+                                    }
+                                    if (_this._parent.onBeforeMaterialReadyAsync) {
+                                        _this._addLoaderPendingData(material);
+                                        _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
                                             babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                        }
-                                    });
-                                }
+                                            _this._removeLoaderPendingData(material);
+                                        });
+                                    }
+                                    else {
+                                        babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                    }
+                                });
                             }
                         });
                         vertexData.merge(subVertexData);
@@ -3967,14 +3962,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._traverseNode = function (index, action, parentNode) {
                 if (parentNode === void 0) { parentNode = null; }
+                if (GLTF2.GLTFLoaderExtension.TraverseNode(this, index, action, parentNode)) {
+                    return;
+                }
                 var node = this._gltf.nodes[index];
                 if (!action(node, index, parentNode)) {
                     return;
                 }
                 if (node.children) {
-                    for (var i = 0; i < node.children.length; i++) {
-                        this._traverseNode(node.children[i], action, node);
-                    }
+                    this._traverseNodes(node.children, action, node);
                 }
             };
             GLTFLoader.prototype._loadAnimations = function () {
@@ -4102,11 +4098,11 @@ var BABYLON;
             GLTFLoader.prototype._loadBufferAsync = function (index, onSuccess) {
                 var _this = this;
                 var buffer = this._gltf.buffers[index];
-                this.addPendingData(buffer);
+                this._addPendingData(buffer);
                 if (buffer.loadedData) {
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(buffer.uri)) {
@@ -4114,20 +4110,20 @@ var BABYLON;
                     buffer.loadedData = new Uint8Array(data);
                     setTimeout(function () {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else if (buffer.loadedObservable) {
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                 }
                 else {
                     buffer.loadedObservable = new BABYLON.Observable();
                     buffer.loadedObservable.add(function (buffer) {
                         onSuccess(buffer.loadedData);
-                        _this.removePendingData(buffer);
+                        _this._removePendingData(buffer);
                     });
                     BABYLON.Tools.LoadFile(this._rootUrl + buffer.uri, function (data) {
                         buffer.loadedData = new Uint8Array(data);
@@ -4140,7 +4136,7 @@ var BABYLON;
                     }, this._babylonScene.database, true, function (request) {
                         if (!_this._disposed) {
                             _this._onError("Failed to load file '" + buffer.uri + "': " + request.status + " " + request.statusText);
-                            _this.removePendingData(buffer);
+                            _this._removePendingData(buffer);
                         }
                     });
                 }
@@ -4269,54 +4265,42 @@ var BABYLON;
                         return 0;
                 }
             };
-            Object.defineProperty(GLTFLoader.prototype, "blockPendingTracking", {
-                set: function (value) {
-                    this._blockPendingTracking = value;
-                },
-                enumerable: true,
-                configurable: true
-            });
-            GLTFLoader.prototype.addPendingData = function (data) {
+            GLTFLoader.prototype._addPendingData = function (data) {
                 if (!this._renderReady) {
                     this._renderPendingCount++;
                 }
-                this.addLoaderPendingData(data);
+                this._addLoaderPendingData(data);
             };
-            GLTFLoader.prototype.removePendingData = function (data) {
+            GLTFLoader.prototype._removePendingData = function (data) {
                 if (!this._renderReady) {
                     if (--this._renderPendingCount === 0) {
                         this._renderReady = true;
                         this._onRenderReady();
                     }
                 }
-                this.removeLoaderPendingData(data);
-            };
-            GLTFLoader.prototype.addLoaderNonBlockingPendingData = function (data) {
-                if (!this._nonBlockingData) {
-                    this._nonBlockingData = new Array();
-                }
-                this._nonBlockingData.push(data);
+                this._removeLoaderPendingData(data);
             };
-            GLTFLoader.prototype.addLoaderPendingData = function (data) {
-                if (this._blockPendingTracking) {
-                    this.addLoaderNonBlockingPendingData(data);
-                    return;
-                }
+            GLTFLoader.prototype._addLoaderPendingData = function (data) {
                 this._loaderPendingCount++;
+                this._loaderTrackers.forEach(function (tracker) { return tracker._addPendingData(data); });
             };
-            GLTFLoader.prototype.removeLoaderPendingData = function (data) {
-                var indexInPending = this._nonBlockingData ? this._nonBlockingData.indexOf(data) : -1;
-                if (indexInPending !== -1) {
-                    this._nonBlockingData.splice(indexInPending, 1);
-                }
-                else if (--this._loaderPendingCount === 0) {
-                    this._onLoaderFirstLODComplete();
-                }
-                if ((!this._nonBlockingData || this._nonBlockingData.length === 0) && this._loaderPendingCount === 0) {
-                    this._onLoaderComplete();
-                    this.dispose();
+            GLTFLoader.prototype._removeLoaderPendingData = function (data) {
+                this._loaderTrackers.forEach(function (tracker) { return tracker._removePendingData(data); });
+                if (--this._loaderPendingCount === 0) {
+                    this._onComplete();
                 }
             };
+            GLTFLoader.prototype._whenAction = function (action, onComplete) {
+                var _this = this;
+                var tracker = new GLTFLoaderTracker(function () {
+                    _this._loaderTrackers.splice(_this._loaderTrackers.indexOf(tracker));
+                    onComplete();
+                });
+                this._loaderTrackers.push(tracker);
+                this._addLoaderPendingData(tracker);
+                action();
+                this._removeLoaderPendingData(tracker);
+            };
             GLTFLoader.prototype._getDefaultMaterial = function () {
                 if (!this._defaultMaterial) {
                     var id = "__gltf_default";
@@ -4344,17 +4328,17 @@ var BABYLON;
                 babylonMaterial.metallic = properties.metallicFactor === undefined ? 1 : properties.metallicFactor;
                 babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
                 if (properties.baseColorTexture) {
-                    babylonMaterial.albedoTexture = this.loadTexture(properties.baseColorTexture);
+                    babylonMaterial.albedoTexture = this._loadTexture(properties.baseColorTexture);
                 }
                 if (properties.metallicRoughnessTexture) {
-                    babylonMaterial.metallicTexture = this.loadTexture(properties.metallicRoughnessTexture);
+                    babylonMaterial.metallicTexture = this._loadTexture(properties.metallicRoughnessTexture);
                     babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
                     babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
                     babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
                 }
-                this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
+                this._loadMaterialAlphaProperties(material, properties.baseColorFactor);
             };
-            GLTFLoader.prototype.loadMaterial = function (material, assign) {
+            GLTFLoader.prototype._loadMaterial = function (material, assign) {
                 if (material.babylonMaterial) {
                     assign(material.babylonMaterial, false);
                     return;
@@ -4362,17 +4346,17 @@ var BABYLON;
                 if (GLTF2.GLTFLoaderExtension.LoadMaterial(this, material, assign)) {
                     return;
                 }
-                this.createPbrMaterial(material);
-                this.loadMaterialBaseProperties(material);
+                this._createPbrMaterial(material);
+                this._loadMaterialBaseProperties(material);
                 this._loadMaterialMetallicRoughnessProperties(material);
                 assign(material.babylonMaterial, true);
             };
-            GLTFLoader.prototype.createPbrMaterial = function (material) {
+            GLTFLoader.prototype._createPbrMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "mat" + material.index, this._babylonScene);
                 babylonMaterial.sideOrientation = BABYLON.Material.CounterClockWiseSideOrientation;
                 material.babylonMaterial = babylonMaterial;
             };
-            GLTFLoader.prototype.loadMaterialBaseProperties = function (material) {
+            GLTFLoader.prototype._loadMaterialBaseProperties = function (material) {
                 var babylonMaterial = material.babylonMaterial;
                 babylonMaterial.emissiveColor = material.emissiveFactor ? BABYLON.Color3.FromArray(material.emissiveFactor) : new BABYLON.Color3(0, 0, 0);
                 if (material.doubleSided) {
@@ -4380,7 +4364,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
+                    babylonMaterial.bumpTexture = this._loadTexture(material.normalTexture);
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
                     babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
                     if (material.normalTexture.scale !== undefined) {
@@ -4388,17 +4372,17 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    babylonMaterial.ambientTexture = this.loadTexture(material.occlusionTexture);
+                    babylonMaterial.ambientTexture = this._loadTexture(material.occlusionTexture);
                     babylonMaterial.useAmbientInGrayScale = true;
                     if (material.occlusionTexture.strength !== undefined) {
                         babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
                     }
                 }
                 if (material.emissiveTexture) {
-                    babylonMaterial.emissiveTexture = this.loadTexture(material.emissiveTexture);
+                    babylonMaterial.emissiveTexture = this._loadTexture(material.emissiveTexture);
                 }
             };
-            GLTFLoader.prototype.loadMaterialAlphaProperties = function (material, colorFactor) {
+            GLTFLoader.prototype._loadMaterialAlphaProperties = function (material, colorFactor) {
                 var babylonMaterial = material.babylonMaterial;
                 var alphaMode = material.alphaMode || "OPAQUE";
                 switch (alphaMode) {
@@ -4421,7 +4405,7 @@ var BABYLON;
                 }
                 babylonMaterial.alphaCutOff = material.alphaCutoff === undefined ? 0.5 : material.alphaCutoff;
             };
-            GLTFLoader.prototype.loadTexture = function (textureInfo) {
+            GLTFLoader.prototype._loadTexture = function (textureInfo) {
                 var _this = this;
                 var texture = this._gltf.textures[textureInfo.index];
                 var texCoord = textureInfo.texCoord || 0;
@@ -4432,15 +4416,15 @@ var BABYLON;
                 var sampler = (texture.sampler === undefined ? {} : this._gltf.samplers[texture.sampler]);
                 var noMipMaps = (sampler.minFilter === GLTF2.ETextureMinFilter.NEAREST || sampler.minFilter === GLTF2.ETextureMinFilter.LINEAR);
                 var samplingMode = GLTF2.GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
-                this.addPendingData(texture);
+                this._addPendingData(texture);
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, noMipMaps, false, samplingMode, function () {
                     if (!_this._disposed) {
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 }, function () {
                     if (!_this._disposed) {
                         _this._onError("Failed to load texture '" + source.uri + "'");
-                        _this.removePendingData(texture);
+                        _this._removePendingData(texture);
                     }
                 });
                 if (texture.url) {
@@ -4607,9 +4591,34 @@ var BABYLON;
             function GLTFLoaderExtension() {
                 this.enabled = true;
             }
-            GLTFLoaderExtension.prototype.loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._traverseNode = function (loader, index, action, parentNode) { return false; };
+            GLTFLoaderExtension.prototype._loadNode = function (loader, node) { return false; };
+            GLTFLoaderExtension.prototype._loadMaterial = function (loader, material, assign) { return false; };
+            GLTFLoaderExtension.prototype._loadExtension = function (property, action) {
+                var _this = this;
+                if (!property.extensions) {
+                    return false;
+                }
+                var extension = property.extensions[this.name];
+                if (!extension) {
+                    return false;
+                }
+                // Clear out the extension before executing the action to avoid recursing into the same property.
+                property.extensions[this.name] = undefined;
+                action(extension, function () {
+                    // Restore the extension after completing the action.
+                    property.extensions[_this.name] = extension;
+                });
+                return true;
+            };
+            GLTFLoaderExtension.TraverseNode = function (loader, index, action, parentNode) {
+                return this._ApplyExtensions(function (extension) { return extension._traverseNode(loader, index, action, parentNode); });
+            };
+            GLTFLoaderExtension.LoadNode = function (loader, node) {
+                return this._ApplyExtensions(function (extension) { return extension._loadNode(loader, node); });
+            };
             GLTFLoaderExtension.LoadMaterial = function (loader, material, assign) {
-                return this._ApplyExtensions(function (extension) { return extension.loadMaterial(loader, material, assign); });
+                return this._ApplyExtensions(function (extension) { return extension._loadMaterial(loader, material, assign); });
             };
             GLTFLoaderExtension._ApplyExtensions = function (action) {
                 var extensions = GLTFLoaderExtension._Extensions;
@@ -4644,6 +4653,7 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // See https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod for more information about this extension.
             var MSFTLOD = (function (_super) {
                 __extends(MSFTLOD, _super);
                 function MSFTLOD() {
@@ -4656,49 +4666,71 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                MSFTLOD.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    // Clear out the extension so that it won't get loaded again.
-                    material.extensions[this.name] = undefined;
-                    // Tell the loader not to clear its state until the highest LOD is loaded.
-                    var materialLODs = [material.index].concat(properties.ids);
-                    loader.addLoaderPendingData(material);
-                    for (var index = 0; index < materialLODs.length; index++) {
-                        loader.addLoaderNonBlockingPendingData(index);
-                    }
-                    // Start with the lowest quality LOD.
-                    this.loadMaterialLOD(loader, material, materialLODs, materialLODs.length - 1, assign);
-                    return true;
+                MSFTLOD.prototype._traverseNode = function (loader, index, action, parentNode) {
+                    var node = loader._gltf.nodes[index];
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        for (var i = extension.ids.length - 1; i >= 0; i--) {
+                            loader._traverseNode(extension.ids[i], action, parentNode);
+                        }
+                        loader._traverseNode(index, action, parentNode);
+                        onComplete();
+                    });
                 };
-                MSFTLOD.prototype.loadMaterialLOD = function (loader, material, materialLODs, lod, assign) {
+                MSFTLOD.prototype._loadNode = function (loader, node) {
                     var _this = this;
-                    var materialLOD = loader.gltf.materials[materialLODs[lod]];
-                    if (lod !== materialLODs.length - 1) {
-                        loader.blockPendingTracking = true;
-                    }
-                    loader.loadMaterial(materialLOD, function (babylonMaterial, isNew) {
-                        assign(babylonMaterial, isNew);
-                        loader.removeLoaderPendingData(lod);
-                        // Loading is considered complete if this is the lowest quality LOD.
-                        if (lod === materialLODs.length - 1) {
-                            loader.removeLoaderPendingData(material);
+                    return this._loadExtension(node, function (extension, onComplete) {
+                        var nodes = [node.index].concat(extension.ids).map(function (index) { return loader._gltf.nodes[index]; });
+                        loader._addLoaderPendingData(node);
+                        _this._loadNodeLOD(loader, nodes, nodes.length - 1, function () {
+                            loader._removeLoaderPendingData(node);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadNodeLOD = function (loader, nodes, index, onComplete) {
+                    var _this = this;
+                    loader._whenAction(function () {
+                        loader._loadNode(nodes[index]);
+                    }, function () {
+                        if (index !== nodes.length - 1) {
+                            var previousNode = nodes[index + 1];
+                            previousNode.babylonMesh.setEnabled(false);
+                        }
+                        if (index === 0) {
+                            onComplete();
+                            return;
                         }
-                        if (lod === 0) {
-                            loader.blockPendingTracking = false;
+                        setTimeout(function () {
+                            _this._loadNodeLOD(loader, nodes, index - 1, onComplete);
+                        }, MSFTLOD.MinimalLODDelay);
+                    });
+                };
+                MSFTLOD.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        var materials = [material.index].concat(extension.ids).map(function (index) { return loader._gltf.materials[index]; });
+                        loader._addLoaderPendingData(material);
+                        _this._loadMaterialLOD(loader, materials, materials.length - 1, assign, function () {
+                            material.extensions[_this.name] = extension;
+                            loader._removeLoaderPendingData(material);
+                            onComplete();
+                        });
+                    });
+                };
+                MSFTLOD.prototype._loadMaterialLOD = function (loader, materials, index, assign, onComplete) {
+                    var _this = this;
+                    loader._loadMaterial(materials[index], function (babylonMaterial, isNew) {
+                        assign(babylonMaterial, isNew);
+                        if (index === 0) {
+                            onComplete();
                             return;
                         }
                         // Load the next LOD when the loader is ready to render and
                         // all active material textures of the current LOD are loaded.
-                        loader.executeWhenRenderReady(function () {
+                        loader._executeWhenRenderReady(function () {
                             BABYLON.BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), function () {
                                 setTimeout(function () {
-                                    _this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
+                                    _this._loadMaterialLOD(loader, materials, index - 1, assign, onComplete);
                                 }, MSFTLOD.MinimalLODDelay);
                             });
                         });
@@ -4738,19 +4770,14 @@ var BABYLON;
                     enumerable: true,
                     configurable: true
                 });
-                KHRMaterialsPbrSpecularGlossiness.prototype.loadMaterial = function (loader, material, assign) {
-                    if (!material.extensions) {
-                        return false;
-                    }
-                    var properties = material.extensions[this.name];
-                    if (!properties) {
-                        return false;
-                    }
-                    loader.createPbrMaterial(material);
-                    loader.loadMaterialBaseProperties(material);
-                    this._loadSpecularGlossinessProperties(loader, material, properties);
-                    assign(material.babylonMaterial, true);
-                    return true;
+                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterial = function (loader, material, assign) {
+                    var _this = this;
+                    return this._loadExtension(material, function (extension, onComplete) {
+                        loader._createPbrMaterial(material);
+                        loader._loadMaterialBaseProperties(material);
+                        _this._loadSpecularGlossinessProperties(loader, material, extension);
+                        assign(material.babylonMaterial, true);
+                    });
                 };
                 KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessProperties = function (loader, material, properties) {
                     var babylonMaterial = material.babylonMaterial;
@@ -4758,14 +4785,14 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : new BABYLON.Color3(1, 1, 1);
                     babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        babylonMaterial.albedoTexture = loader.loadTexture(properties.diffuseTexture);
+                        babylonMaterial.albedoTexture = loader._loadTexture(properties.diffuseTexture);
                     }
                     if (properties.specularGlossinessTexture) {
-                        babylonMaterial.reflectivityTexture = loader.loadTexture(properties.specularGlossinessTexture);
+                        babylonMaterial.reflectivityTexture = loader._loadTexture(properties.specularGlossinessTexture);
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader.loadMaterialAlphaProperties(material, properties.diffuseFactor);
+                    loader._loadMaterialAlphaProperties(material, properties.diffuseFactor);
                 };
                 return KHRMaterialsPbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 37 - 33
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -125,13 +125,17 @@ declare module BABYLON {
          */
         onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
         /**
-         * Raised when all LODs are complete (or if there is no LOD and model is complete)
+         * Raised when the visible components (geometry, materials, textures, etc.) are first ready to be rendered.
+         * For assets with LODs, raised when the first LOD is complete.
+         * For assets without LODs, raised when the model is complete just before onComplete.
          */
-        onComplete: () => void;
+        onReady: () => void;
         /**
-         * Raised when first LOD complete (or if there is no LOD and model is complete)
+         * Raised when the asset is completely loaded, just 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 just after onReady.
          */
-        onFirstLODComplete: () => void;
+        onComplete: () => void;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -884,9 +888,9 @@ declare module BABYLON.GLTF2 {
 
 declare module BABYLON.GLTF2 {
     class GLTFLoader implements IGLTFLoader, IDisposable {
+        _gltf: IGLTF;
+        _babylonScene: Scene;
         private _parent;
-        private _gltf;
-        private _babylonScene;
         private _rootUrl;
         private _defaultMaterial;
         private _successCallback;
@@ -894,19 +898,14 @@ declare module BABYLON.GLTF2 {
         private _errorCallback;
         private _renderReady;
         private _disposed;
-        private _blockPendingTracking;
-        private _nonBlockingData;
-        private _rootMesh;
         private _renderReadyObservable;
         private _renderPendingCount;
         private _loaderPendingCount;
+        private _loaderTrackers;
         static Extensions: {
             [name: string]: GLTFLoaderExtension;
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
-        readonly gltf: IGLTF;
-        readonly babylonScene: Scene;
-        executeWhenRenderReady(func: () => void): void;
         constructor(parent: GLTFFileLoader);
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -914,18 +913,16 @@ declare module BABYLON.GLTF2 {
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess, onProgress, onError);
         private _onError(message);
         private _onProgress(event);
+        _executeWhenRenderReady(func: () => void): void;
         private _onRenderReady();
-        private _onLoaderComplete();
-        private _onLoaderFirstLODComplete();
+        private _onComplete();
         private _loadData(data);
-        private _addRightHandToLeftHandRootTransform();
         private _getMeshes();
         private _getSkeletons();
         private _getAnimationTargets();
-        private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
-        private _loadNode(node);
+        _loadNode(node: IGLTFNode): void;
         private _loadMesh(node, mesh);
         private _loadVertexDataAsync(primitive, onSuccess);
         private _createMorphTargets(node, mesh, primitive, babylonMesh);
@@ -937,7 +934,7 @@ declare module BABYLON.GLTF2 {
         private _loadBone(node, skin, inverseBindMatrixData, babylonBones);
         private _getNodeMatrix(node);
         private _traverseNodes(indices, action, parentNode?);
-        private _traverseNode(index, action, parentNode?);
+        _traverseNode(index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode?: IGLTFNode): void;
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
@@ -951,19 +948,18 @@ declare module BABYLON.GLTF2 {
         private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
-        blockPendingTracking: boolean;
-        addPendingData(data: any): void;
-        removePendingData(data: any): void;
-        addLoaderNonBlockingPendingData(data: any): void;
-        addLoaderPendingData(data: any): void;
-        removeLoaderPendingData(data: any): void;
+        _addPendingData(data: any): void;
+        _removePendingData(data: any): void;
+        _addLoaderPendingData(data: any): void;
+        _removeLoaderPendingData(data: any): void;
+        _whenAction(action: () => void, onComplete: () => void): void;
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessProperties(material);
-        loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
-        createPbrMaterial(material: IGLTFMaterial): void;
-        loadMaterialBaseProperties(material: IGLTFMaterial): void;
-        loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;
-        loadTexture(textureInfo: IGLTFTextureInfo): Texture;
+        _loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
+        _createPbrMaterial(material: IGLTFMaterial): void;
+        _loadMaterialBaseProperties(material: IGLTFMaterial): void;
+        _loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;
+        _loadTexture(textureInfo: IGLTFTextureInfo): Texture;
     }
 }
 
@@ -999,8 +995,13 @@ declare module BABYLON.GLTF2 {
     abstract class GLTFLoaderExtension {
         enabled: boolean;
         readonly abstract name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _loadExtension<T>(property: IGLTFProperty, action: (extension: T, onComplete: () => void) => void): boolean;
         static _Extensions: GLTFLoaderExtension[];
+        static TraverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        static LoadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
         static LoadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
         private static _ApplyExtensions(action);
     }
@@ -1014,8 +1015,11 @@ declare module BABYLON.GLTF2.Extensions {
          */
         static MinimalLODDelay: number;
         readonly name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
-        private loadMaterialLOD(loader, material, materialLODs, lod, assign);
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean;
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean;
+        private _loadNodeLOD(loader, nodes, index, onComplete);
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        private _loadMaterialLOD(loader, materials, index, assign, onComplete);
     }
 }
 
@@ -1023,7 +1027,7 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean;
         private _loadSpecularGlossinessProperties(loader, material, properties);
     }
 }

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js


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

@@ -13,10 +13,12 @@
 - New InputText for Babylon.GUI. [Doc here](http://doc.babylonjs.com/overviews/gui#inputtext) ([deltakosh](https://github.com/deltakosh))
 - New VirtualKeyboard for Babylon.GUI. [Doc here](http://doc.babylonjs.com/overviews/gui#virtualkeyboard) ([deltakosh](https://github.com/deltakosh) / [adam](https://github.com/abow))
 - Added support for depth pre-pass rendering. [Doc here](http://doc.babylonjs.com/tutorials/transparency_and_how_meshes_are_rendered#depth-pre-pass-meshes) ([deltakosh](https://github.com/deltakosh))
+- Added support for `material.separateCullingPass`. [Doc here](http://doc.babylonjs.com/tutorials/transparency_and_how_meshes_are_rendered#things-to-do-and-not-to-do) ([sebavan](https://github.com/sebavan))
 - Added support for Windows Motion Controllers ([Lewis Weaver](https://github.com/leweaver))
-- Added support for Particle animation in ParticleSystem ([Ibraheem Osama](https://github.com/IbraheemOsama))
+- Added support for Particle animation in ParticleSystem. [Doc here](http://doc.babylonjs.com/tutorials/particles#particle-animation) ([Ibraheem Osama](https://github.com/IbraheemOsama))
 
 ## Updates
+- Several inspector improvments ([temechon](https://github.com/temechon))
 - New observables for actions: `onBeforeExecuteObservable` for all actions and `onInterpolationDoneObservable` for `InterpolateValueAction` ([deltakosh](https://github.com/deltakosh))
 - New observables for gamepads: `onButtonDownObservable`, `onButtonUpObservable`, `onPadDownObservable`, `onPadUpObservable` ([deltakosh](https://github.com/deltakosh))
 - New `camera.storeState()` and `camera.restoreState()` functions to store / restore cameras position / rotation / fov. (Doc here)[http://doc.babylonjs.com/tutorials/cameras#state] ([deltakosh](https://github.com/deltakosh))

+ 10 - 18
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -14,21 +14,13 @@ module BABYLON.GLTF2.Extensions {
             return "KHR_materials_pbrSpecularGlossiness";
         }
 
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean {
-            if (!material.extensions) {
-                return false;
-            }
-
-            var properties = material.extensions[this.name] as IKHRMaterialsPbrSpecularGlossiness;
-            if (!properties) {
-                return false;
-            }
-
-            loader.createPbrMaterial(material);
-            loader.loadMaterialBaseProperties(material);
-            this._loadSpecularGlossinessProperties(loader, material, properties);
-            assign(material.babylonMaterial, true);
-            return true;
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean {
+            return this._loadExtension<IKHRMaterialsPbrSpecularGlossiness>(material, (extension, onComplete) => {
+                loader._createPbrMaterial(material);
+                loader._loadMaterialBaseProperties(material);
+                this._loadSpecularGlossinessProperties(loader, material, extension);
+                assign(material.babylonMaterial, true);
+            });
         }
 
         private _loadSpecularGlossinessProperties(loader: GLTFLoader, material: IGLTFMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): void {
@@ -39,16 +31,16 @@ module BABYLON.GLTF2.Extensions {
             babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
 
             if (properties.diffuseTexture) {
-                babylonMaterial.albedoTexture = loader.loadTexture(properties.diffuseTexture);
+                babylonMaterial.albedoTexture = loader._loadTexture(properties.diffuseTexture);
             }
 
             if (properties.specularGlossinessTexture) {
-                babylonMaterial.reflectivityTexture = loader.loadTexture(properties.specularGlossinessTexture);
+                babylonMaterial.reflectivityTexture = loader._loadTexture(properties.specularGlossinessTexture);
                 babylonMaterial.reflectivityTexture.hasAlpha = true;
                 babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
             }
 
-            loader.loadMaterialAlphaProperties(material, properties.diffuseFactor);
+            loader._loadMaterialAlphaProperties(material, properties.diffuseFactor);
         }
     }
 

+ 57 - 39
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -5,6 +5,7 @@ module BABYLON.GLTF2.Extensions {
         ids: number[];
     }
 
+    // See https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod for more information about this extension.
     export class MSFTLOD extends GLTFLoaderExtension {
         /**
          * Specify the minimal delay between LODs in ms (default = 250)
@@ -15,66 +16,83 @@ module BABYLON.GLTF2.Extensions {
             return "MSFT_lod";
         }
 
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean {
-            if (!material.extensions) {
-                return false;
-            }
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean {
+            var node = loader._gltf.nodes[index];
 
-            var properties = material.extensions[this.name] as IMSFTLOD;
-            if (!properties) {
-                return false;
-            }
+            return this._loadExtension<IMSFTLOD>(node, (extension, onComplete) => {
+                for (var i = extension.ids.length - 1; i >= 0; i--) {
+                    loader._traverseNode(extension.ids[i], action, parentNode);
+                }
 
-            // Clear out the extension so that it won't get loaded again.
-            material.extensions[this.name] = undefined;
+                loader._traverseNode(index, action, parentNode);
+                onComplete();
+            });
+        }
 
-            // Tell the loader not to clear its state until the highest LOD is loaded.
-            var materialLODs = [material.index, ...properties.ids];
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean {
+            return this._loadExtension<IMSFTLOD>(node, (extension, onComplete) => {
+                var nodes = [node.index, ...extension.ids].map(index => loader._gltf.nodes[index]);
 
-            loader.addLoaderPendingData(material);
-            for (var index = 0; index < materialLODs.length; index++) {
-                loader.addLoaderNonBlockingPendingData(index);
-            }
+                loader._addLoaderPendingData(node);
+                this._loadNodeLOD(loader, nodes, nodes.length - 1, () => {
+                    loader._removeLoaderPendingData(node);
+                    onComplete();
+                });
+            });
+        }
 
-            // Start with the lowest quality LOD.
-            this.loadMaterialLOD(loader, material, materialLODs, materialLODs.length - 1, assign);
+        private _loadNodeLOD(loader: GLTFLoader, nodes: IGLTFNode[], index: number, onComplete: () => void): void {
+            loader._whenAction(() => {
+                loader._loadNode(nodes[index]);
+            }, () => {
+                if (index !== nodes.length - 1) {
+                    var previousNode = nodes[index + 1];
+                    previousNode.babylonMesh.setEnabled(false);
+                }
 
-            return true;
-        }
+                if (index === 0) {
+                    onComplete();
+                    return;
+                }
 
-        private loadMaterialLOD(loader: GLTFLoader, material: IGLTFMaterial, materialLODs: number[], lod: number, assign: (babylonMaterial: Material, isNew: boolean) => void): void {
-            var materialLOD = loader.gltf.materials[materialLODs[lod]];
+                setTimeout(() => {
+                    this._loadNodeLOD(loader, nodes, index - 1, onComplete);
+                }, MSFTLOD.MinimalLODDelay);
+            });
+        }
 
-            if (lod !== materialLODs.length - 1) {
-                loader.blockPendingTracking = true;
-            }
-            
-            loader.loadMaterial(materialLOD, (babylonMaterial, isNew) => {
-                assign(babylonMaterial, isNew);
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean {
+            return this._loadExtension<IMSFTLOD>(material, (extension, onComplete) => {
+                var materials = [material.index, ...extension.ids].map(index => loader._gltf.materials[index]);
 
-                loader.removeLoaderPendingData(lod);
+                loader._addLoaderPendingData(material);
+                this._loadMaterialLOD(loader, materials, materials.length - 1, assign, () => {
+                    material.extensions[this.name] = extension;
+                    loader._removeLoaderPendingData(material);
+                    onComplete();
+                });
+            });
+        }
 
-                // Loading is considered complete if this is the lowest quality LOD.
-                if (lod === materialLODs.length - 1) {
-                    loader.removeLoaderPendingData(material);
-                }
+        private _loadMaterialLOD(loader: GLTFLoader, materials: IGLTFMaterial[], index: number, assign: (babylonMaterial: Material, isNew: boolean) => void, onComplete: () => void): void {
+            loader._loadMaterial(materials[index], (babylonMaterial, isNew) => {
+                assign(babylonMaterial, isNew);
 
-                if (lod === 0) {
-                    loader.blockPendingTracking = false;
+                if (index === 0) {
+                    onComplete();
                     return;
                 }
 
                 // Load the next LOD when the loader is ready to render and
                 // all active material textures of the current LOD are loaded.
-                loader.executeWhenRenderReady(() => {
+                loader._executeWhenRenderReady(() => {
                     BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), () => {
-                        setTimeout(()=> {
-                            this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
+                        setTimeout(() => {
+                            this._loadMaterialLOD(loader, materials, index - 1, assign, onComplete);
                         }, MSFTLOD.MinimalLODDelay);
                     });
                 });
             });
-
         }
     }
 

+ 185 - 179
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -1,29 +1,47 @@
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GLTF2 {
+    class GLTFLoaderTracker {
+        private _pendingCount = 0;
+        private _callback: () => void;
+
+        constructor(onComplete: () => void) {
+            this._callback = onComplete;
+        }
+
+        public _addPendingData(data: any): void {
+            this._pendingCount++;
+        }
+
+        public _removePendingData(data: any): void {
+            if (--this._pendingCount === 0) {
+                this._callback();
+            }
+        }
+   }
+
     export class GLTFLoader implements IGLTFLoader, IDisposable {
+        public _gltf: IGLTF;
+        public _babylonScene: Scene;
+
         private _parent: GLTFFileLoader;
-        private _gltf: IGLTF;
-        private _babylonScene: Scene;
         private _rootUrl: string;
         private _defaultMaterial: PBRMaterial;
         private _successCallback: () => void;
         private _progressCallback: (event: ProgressEvent) => void;
         private _errorCallback: (message: string) => void;
-        private _renderReady: boolean = false;
-        private _disposed: boolean = false;
-        private _blockPendingTracking: boolean = false;
-        private _nonBlockingData: Array<any>;
-        private _rootMesh: Mesh;
+        private _renderReady = false;
+        private _disposed = false;
 
-        // Observable with boolean indicating success or error.
         private _renderReadyObservable = new Observable<GLTFLoader>();
 
         // Count of pending work that needs to complete before the asset is rendered.
-        private _renderPendingCount: number = 0;
+        private _renderPendingCount = 0;
 
-        // Count of pending work that needs to complete before the loader is cleared.
-        private _loaderPendingCount: number = 0;
+        // Count of pending work that needs to complete before the loader is disposed.
+        private _loaderPendingCount = 0;
+
+        private _loaderTrackers = new Array<GLTFLoaderTracker>();
 
         public static Extensions: { [name: string]: GLTFLoaderExtension } = {};
 
@@ -39,23 +57,6 @@ module BABYLON.GLTF2 {
             GLTFLoaderExtension._Extensions.push(extension);
         }
 
-        public get gltf(): IGLTF {
-            return this._gltf;
-        }
-
-        public get babylonScene(): Scene {
-            return this._babylonScene;
-        }
-
-        public executeWhenRenderReady(func: () => void) {
-            if (this._renderReady) {
-                func();
-            }
-            else {
-                this._renderReadyObservable.add(func);
-            }
-        }
-
         public constructor(parent: GLTFFileLoader) {
             this._parent = parent;
         }
@@ -107,57 +108,54 @@ module BABYLON.GLTF2 {
             this._progressCallback = onProgress;
             this._errorCallback = onError;
 
-            this.addPendingData(this);
+            this._addPendingData(this);
             this._loadScene(nodeNames);
             this._loadAnimations();
-            this.removePendingData(this);
+            this._removePendingData(this);
         }
 
         private _onError(message: string): void {
             if (this._errorCallback) {
                 this._errorCallback(message);
             }
+
             this.dispose();
         }
 
         private _onProgress(event: ProgressEvent): void {
-            this._progressCallback(event);
+            if (this._progressCallback) {
+                this._progressCallback(event);
+            }
         }
 
-        private _onRenderReady(): void {
-            switch (this._parent.coordinateSystemMode) {
-                case GLTFLoaderCoordinateSystemMode.AUTO:
-                    if (!this._babylonScene.useRightHandedSystem) {
-                        this._addRightHandToLeftHandRootTransform();
-                    }
-                    break;
-                case GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
-                    // do nothing
-                    break;
-                case GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
-                    this._babylonScene.useRightHandedSystem = true;
-                    break;
-                default:
-                    Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
-                    break;
+        public _executeWhenRenderReady(func: () => void): void {
+            if (this._renderReady) {
+                func();
             }
+            else {
+                this._renderReadyObservable.add(func);
+            }
+        }
+
+        private _onRenderReady(): void {
+            var rootNode = this._gltf.nodes[this._gltf.nodes.length - 1];
+            rootNode.babylonMesh.setEnabled(true);
 
-            this._showMeshes();
             this._startAnimations();
             this._successCallback();
             this._renderReadyObservable.notifyObservers(this);
+
+            if (this._parent.onReady) {
+                this._parent.onReady();
+            }
         }
 
-        private _onLoaderComplete(): void {
+        private _onComplete(): void {
             if (this._parent.onComplete) {
                 this._parent.onComplete();
             }
-        }
 
-        private _onLoaderFirstLODComplete(): void {
-            if (this._parent.onFirstLODComplete) {
-                this._parent.onFirstLODComplete();
-            }
+            this.dispose();
         }
 
         private _loadData(data: IGLTFLoaderData): void {
@@ -183,30 +181,9 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _addRightHandToLeftHandRootTransform(): void {
-            this._rootMesh = new Mesh("root", this._babylonScene);
-            this._rootMesh.isVisible = false;
-            this._rootMesh.scaling = new Vector3(1, 1, -1);
-            this._rootMesh.rotation.y = Math.PI;
-
-            var nodes = this._gltf.nodes;
-            if (nodes) {
-                for (var i = 0; i < nodes.length; i++) {
-                    var mesh = nodes[i].babylonMesh;
-                    if (mesh && !mesh.parent) {
-                        mesh.parent = this._rootMesh;
-                    }
-                }
-            }
-        }
-
         private _getMeshes(): Mesh[] {
             var meshes = [];
 
-            if (this._rootMesh) {
-                meshes.push(this._rootMesh);
-            }
-
             var nodes = this._gltf.nodes;
             if (nodes) {
                 nodes.forEach(node => {
@@ -247,16 +224,38 @@ module BABYLON.GLTF2 {
             return targets;
         }
 
-        private _showMeshes(): void {
-            this._getMeshes().forEach(mesh => mesh.isVisible = true);
-        }
-
         private _startAnimations(): void {
             this._getAnimationTargets().forEach(target => this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true));
         }
 
         private _loadScene(nodeNames: any): void {
             var scene = this._gltf.scenes[this._gltf.scene || 0];
+
+            var rootNode = { name: "root", children: scene.nodes } as IGLTFNode;
+
+            switch (this._parent.coordinateSystemMode) {
+                case GLTFLoaderCoordinateSystemMode.AUTO:
+                    if (!this._babylonScene.useRightHandedSystem) {
+                        rootNode.rotation = [0, 1, 0, 0];
+                        rootNode.scale = [1, 1, -1];
+                    }
+                    break;
+                case GLTFLoaderCoordinateSystemMode.PASS_THROUGH:
+                    // do nothing
+                    break;
+                case GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED:
+                    this._babylonScene.useRightHandedSystem = true;
+                    break;
+                default:
+                    Tools.Error("Invalid coordinate system mode (" + this._parent.coordinateSystemMode + ")");
+                    break;
+            }
+
+            // Inject a root node into the scene.
+            this._gltf.nodes = this._gltf.nodes || [];
+            scene.nodes = [this._gltf.nodes.length];
+            this._gltf.nodes.push(rootNode);
+
             var nodeIndices = scene.nodes;
 
             this._traverseNodes(nodeIndices, (node, index, parentNode) => {
@@ -277,23 +276,31 @@ module BABYLON.GLTF2 {
 
                 var filteredNodeIndices = new Array<number>();
                 this._traverseNodes(nodeIndices, node => {
-                    if (nodeNames.indexOf(node.name) === -1) {
-                        return true;
+                    if (nodeNames.indexOf(node.name) !== -1) {
+                        filteredNodeIndices.push(node.index);
+                        return false;
                     }
 
-                    filteredNodeIndices.push(node.index);
-                    return false;
+                    return true;
                 });
 
                 nodeIndices = filteredNodeIndices;
             }
 
-            this._traverseNodes(nodeIndices, node => this._loadNode(node));
+            for (var i = 0; i < nodeIndices.length; i++) {
+                this._loadNode(this._gltf.nodes[nodeIndices[i]]);
+            }
+
+            // Disable the root mesh until the asset is ready to render.
+            rootNode.babylonMesh.setEnabled(false);
         }
 
-        private _loadNode(node: IGLTFNode): boolean {
+        public _loadNode(node: IGLTFNode): void {
+            if (GLTFLoaderExtension.LoadNode(this, node)) {
+                return;
+            }
+
             node.babylonMesh = new Mesh(node.name || "mesh" + node.index, this._babylonScene);
-            node.babylonMesh.isVisible = false;
 
             this._loadTransform(node);
 
@@ -317,7 +324,11 @@ module BABYLON.GLTF2 {
                 // TODO: handle cameras
             }
 
-            return true;
+            if (node.children) {
+                for (var i = 0; i < node.children.length; i++) {
+                    this._loadNode(this._gltf.nodes[node.children[i]]);
+                }
+            }
         }
 
         private _loadMesh(node: IGLTFNode, mesh: IGLTFMesh): void {
@@ -356,25 +367,25 @@ module BABYLON.GLTF2 {
                         loadMaterial: () => {
                             if (primitive.material === undefined) {
                                 babylonMultiMaterial.subMaterials[i] = this._getDefaultMaterial();
+                                return;
                             }
-                            else {
-                                var material = this._gltf.materials[primitive.material];
-                                this.loadMaterial(material, (babylonMaterial, isNew) => {
-                                    if (isNew && this._parent.onMaterialLoaded) {
-                                        this._parent.onMaterialLoaded(babylonMaterial);
-                                    }
-                                    
-                                    if (this._parent.onBeforeMaterialReadyAsync) {
-                                        this.addLoaderPendingData(material);
-                                        this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, () => {
-                                            babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                            this.removeLoaderPendingData(material);
-                                        });
-                                    } else {
+
+                            var material = this._gltf.materials[primitive.material];
+                            this._loadMaterial(material, (babylonMaterial, isNew) => {
+                                if (isNew && this._parent.onMaterialLoaded) {
+                                    this._parent.onMaterialLoaded(babylonMaterial);
+                                }
+
+                                if (this._parent.onBeforeMaterialReadyAsync) {
+                                    this._addLoaderPendingData(material);
+                                    this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, () => {
                                         babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                                    }
-                                });
-                            }
+                                        this._removeLoaderPendingData(material);
+                                    });
+                                } else {
+                                    babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                }
+                            });
                         }
                     });
 
@@ -456,7 +467,7 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _createMorphTargets(node: IGLTFNode, mesh: IGLTFMesh, primitive: IGLTFMeshPrimitive, babylonMesh: Mesh) {
+        private _createMorphTargets(node: IGLTFNode, mesh: IGLTFMesh, primitive: IGLTFMeshPrimitive, babylonMesh: Mesh): void {
             var targets = primitive.targets;
             if (!targets) {
                 return;
@@ -618,17 +629,18 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _traverseNode(index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode = null): void {
-            var node = this._gltf.nodes[index];
+        public _traverseNode(index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode = null): void {
+            if (GLTFLoaderExtension.TraverseNode(this, index, action, parentNode)) {
+                return;
+            }
 
+            var node = this._gltf.nodes[index];
             if (!action(node, index, parentNode)) {
                 return;
             }
 
             if (node.children) {
-                for (var i = 0; i < node.children.length; i++) {
-                    this._traverseNode(node.children[i], action, node);
-                }
+                this._traverseNodes(node.children, action, node);
             }
         }
 
@@ -761,14 +773,12 @@ module BABYLON.GLTF2 {
                 }
             };
 
-            this._loadAccessorAsync(this._gltf.accessors[sampler.input], data =>
-            {
+            this._loadAccessorAsync(this._gltf.accessors[sampler.input], data => {
                 inputData = <Float32Array>data;
                 checkSuccess();
             });
 
-            this._loadAccessorAsync(this._gltf.accessors[sampler.output], data =>
-            {
+            this._loadAccessorAsync(this._gltf.accessors[sampler.output], data => {
                 outputData = <Float32Array>data;
                 checkSuccess();
             });
@@ -776,12 +786,12 @@ module BABYLON.GLTF2 {
 
         private _loadBufferAsync(index: number, onSuccess: (data: ArrayBufferView) => void): void {
             var buffer = this._gltf.buffers[index];
-            this.addPendingData(buffer);
+            this._addPendingData(buffer);
 
             if (buffer.loadedData) {
                 setTimeout(() => {
                     onSuccess(buffer.loadedData);
-                    this.removePendingData(buffer);
+                    this._removePendingData(buffer);
                 });
             }
             else if (GLTFUtils.IsBase64(buffer.uri)) {
@@ -789,20 +799,20 @@ module BABYLON.GLTF2 {
                 buffer.loadedData = new Uint8Array(data);
                 setTimeout(() => {
                     onSuccess(buffer.loadedData);
-                    this.removePendingData(buffer);
+                    this._removePendingData(buffer);
                 });
             }
             else if (buffer.loadedObservable) {
                 buffer.loadedObservable.add(buffer => {
                     onSuccess(buffer.loadedData);
-                    this.removePendingData(buffer);
+                    this._removePendingData(buffer);
                 });
             }
             else {
                 buffer.loadedObservable = new Observable<IGLTFBuffer>();
                 buffer.loadedObservable.add(buffer => {
                     onSuccess(buffer.loadedData);
-                    this.removePendingData(buffer);
+                    this._removePendingData(buffer);
                 });
 
                 Tools.LoadFile(this._rootUrl + buffer.uri, data => {
@@ -816,7 +826,7 @@ module BABYLON.GLTF2 {
                 }, this._babylonScene.database, true, request => {
                     if (!this._disposed) {
                         this._onError("Failed to load file '" + buffer.uri + "': " + request.status + " " + request.statusText);
-                        this.removePendingData(buffer);
+                        this._removePendingData(buffer);
                     }
                 });
             }
@@ -832,7 +842,7 @@ module BABYLON.GLTF2 {
 
             this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
 
-            return targetBuffer;              
+            return targetBuffer;
         }
 
         private _buildUint8ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint8Array {
@@ -845,8 +855,8 @@ module BABYLON.GLTF2 {
 
             this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
 
-            return targetBuffer;              
-        }        
+            return targetBuffer;
+        }
 
         private _buildInt16ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Int16Array {
             if (!byteStride) {
@@ -858,8 +868,8 @@ module BABYLON.GLTF2 {
 
             this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
 
-            return targetBuffer;             
-        }   
+            return targetBuffer;
+        }
 
         private _buildUint16ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint16Array {
             if (!byteStride) {
@@ -871,9 +881,9 @@ module BABYLON.GLTF2 {
 
             this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
 
-            return targetBuffer;             
-        }          
-        
+            return targetBuffer;
+        }
+
         private _buildUint32ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint32Array {
             if (!byteStride) {
                 return new Uint32Array(buffer, byteOffset, byteLength);
@@ -884,9 +894,9 @@ module BABYLON.GLTF2 {
 
             this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
 
-            return targetBuffer;            
-        }     
-        
+            return targetBuffer;
+        }
+
         private _buildFloat32ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Float32Array {
             if (!byteStride) {
                 return new Float32Array(buffer, byteOffset, byteLength);
@@ -898,11 +908,11 @@ module BABYLON.GLTF2 {
             this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
 
             return targetBuffer;
-        }    
-        
+        }
+
         private _extractInterleavedData(sourceBuffer: ArrayBufferView, targetBuffer: ArrayBufferView, bytePerComponent: number, stride: number, length: number): void {
             let tempIndex = 0;
-            let sourceIndex = 0;            
+            let sourceIndex = 0;
             let storageSize = bytePerComponent;
 
             while (tempIndex < length) {
@@ -978,19 +988,15 @@ module BABYLON.GLTF2 {
             }
         }
 
-        public set blockPendingTracking(value: boolean) {
-            this._blockPendingTracking = value;
-        }
-
-        public addPendingData(data: any) {
+        public _addPendingData(data: any): void {
             if (!this._renderReady) {
                 this._renderPendingCount++;
             }
 
-            this.addLoaderPendingData(data);
+            this._addLoaderPendingData(data);
         }
 
-        public removePendingData(data: any) {
+        public _removePendingData(data: any): void {
             if (!this._renderReady) {
                 if (--this._renderPendingCount === 0) {
                     this._renderReady = true;
@@ -998,36 +1004,36 @@ module BABYLON.GLTF2 {
                 }
             }
 
-            this.removeLoaderPendingData(data);
+            this._removeLoaderPendingData(data);
         }
 
-        public addLoaderNonBlockingPendingData(data: any): void {
-            if (!this._nonBlockingData) {
-                this._nonBlockingData = new Array<any>();
-            }
-            this._nonBlockingData.push(data);
+        public _addLoaderPendingData(data: any): void {
+            this._loaderPendingCount++;
+
+            this._loaderTrackers.forEach(tracker => tracker._addPendingData(data));
         }
 
-        public addLoaderPendingData(data: any) {
-            if (this._blockPendingTracking) {
-                this.addLoaderNonBlockingPendingData(data);
-                return;
+        public _removeLoaderPendingData(data: any): void {
+            this._loaderTrackers.forEach(tracker => tracker._removePendingData(data));
+
+            if (--this._loaderPendingCount === 0) {
+                this._onComplete();
             }
-            this._loaderPendingCount++;
         }
 
-        public removeLoaderPendingData(data: any) {
-            var indexInPending = this._nonBlockingData ? this._nonBlockingData.indexOf(data) : -1;
-            if (indexInPending !== -1) {
-                this._nonBlockingData.splice(indexInPending, 1);
-            } else if (--this._loaderPendingCount === 0) {
-                this._onLoaderFirstLODComplete();
-            }
+        public _whenAction(action: () => void, onComplete: () => void): void {
+            var tracker = new GLTFLoaderTracker(() => {
+                this._loaderTrackers.splice(this._loaderTrackers.indexOf(tracker));
+                onComplete();
+            });
 
-            if ((!this._nonBlockingData || this._nonBlockingData.length === 0) && this._loaderPendingCount === 0) {
-                this._onLoaderComplete();
-                this.dispose();
-            }
+            this._loaderTrackers.push(tracker);
+
+            this._addLoaderPendingData(tracker);
+
+            action();
+
+            this._removeLoaderPendingData(tracker);
         }
 
         private _getDefaultMaterial(): Material {
@@ -1064,20 +1070,20 @@ module BABYLON.GLTF2 {
             babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
 
             if (properties.baseColorTexture) {
-                babylonMaterial.albedoTexture = this.loadTexture(properties.baseColorTexture);
+                babylonMaterial.albedoTexture = this._loadTexture(properties.baseColorTexture);
             }
 
             if (properties.metallicRoughnessTexture) {
-                babylonMaterial.metallicTexture = this.loadTexture(properties.metallicRoughnessTexture);
+                babylonMaterial.metallicTexture = this._loadTexture(properties.metallicRoughnessTexture);
                 babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
                 babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
                 babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
             }
 
-            this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
+            this._loadMaterialAlphaProperties(material, properties.baseColorFactor);
         }
 
-        public loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void {
+        public _loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void {
             if (material.babylonMaterial) {
                 assign(material.babylonMaterial, false);
                 return;
@@ -1087,19 +1093,19 @@ module BABYLON.GLTF2 {
                 return;
             }
 
-            this.createPbrMaterial(material);
-            this.loadMaterialBaseProperties(material);
+            this._createPbrMaterial(material);
+            this._loadMaterialBaseProperties(material);
             this._loadMaterialMetallicRoughnessProperties(material);
             assign(material.babylonMaterial, true);
         }
 
-        public createPbrMaterial(material: IGLTFMaterial): void {
+        public _createPbrMaterial(material: IGLTFMaterial): void {
             var babylonMaterial = new PBRMaterial(material.name || "mat" + material.index, this._babylonScene);
             babylonMaterial.sideOrientation = Material.CounterClockWiseSideOrientation;
             material.babylonMaterial = babylonMaterial;
         }
 
-        public loadMaterialBaseProperties(material: IGLTFMaterial): void {
+        public _loadMaterialBaseProperties(material: IGLTFMaterial): void {
             var babylonMaterial = material.babylonMaterial as PBRMaterial;
 
             babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0);
@@ -1109,7 +1115,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.normalTexture) {
-                babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
+                babylonMaterial.bumpTexture = this._loadTexture(material.normalTexture);
                 babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
                 babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
                 if (material.normalTexture.scale !== undefined) {
@@ -1118,7 +1124,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.occlusionTexture) {
-                babylonMaterial.ambientTexture = this.loadTexture(material.occlusionTexture);
+                babylonMaterial.ambientTexture = this._loadTexture(material.occlusionTexture);
                 babylonMaterial.useAmbientInGrayScale = true;
                 if (material.occlusionTexture.strength !== undefined) {
                     babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
@@ -1126,11 +1132,11 @@ module BABYLON.GLTF2 {
             }
 
             if (material.emissiveTexture) {
-                babylonMaterial.emissiveTexture = this.loadTexture(material.emissiveTexture);
+                babylonMaterial.emissiveTexture = this._loadTexture(material.emissiveTexture);
             }
         }
 
-        public loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void {
+        public _loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void {
             var babylonMaterial = material.babylonMaterial as PBRMaterial;
 
             var alphaMode = material.alphaMode || "OPAQUE";
@@ -1157,7 +1163,7 @@ module BABYLON.GLTF2 {
             babylonMaterial.alphaCutOff = material.alphaCutoff === undefined ? 0.5 : material.alphaCutoff;
         }
 
-        public loadTexture(textureInfo: IGLTFTextureInfo): Texture {
+        public _loadTexture(textureInfo: IGLTFTextureInfo): Texture {
             var texture = this._gltf.textures[textureInfo.index];
             var texCoord = textureInfo.texCoord || 0;
 
@@ -1170,15 +1176,15 @@ module BABYLON.GLTF2 {
             var noMipMaps = (sampler.minFilter === ETextureMinFilter.NEAREST || sampler.minFilter === ETextureMinFilter.LINEAR);
             var samplingMode = GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
 
-            this.addPendingData(texture);
+            this._addPendingData(texture);
             var babylonTexture = new Texture(null, this._babylonScene, noMipMaps, false, samplingMode, () => {
                 if (!this._disposed) {
-                    this.removePendingData(texture);
+                    this._removePendingData(texture);
                 }
             }, () => {
                 if (!this._disposed) {
                     this._onError("Failed to load texture '" + source.uri + "'");
-                    this.removePendingData(texture);
+                    this._removePendingData(texture);
                 }
             });
 

+ 35 - 2
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -6,7 +6,32 @@ module BABYLON.GLTF2 {
 
         public abstract get name(): string;
 
-        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean { return false; }
+        protected _traverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean { return false; }
+
+        protected _loadNode(loader: GLTFLoader, node: IGLTFNode): boolean { return false; }
+
+        protected _loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean { return false; }
+
+        protected _loadExtension<T>(property: IGLTFProperty, action: (extension: T, onComplete: () => void) => void): boolean {
+            if (!property.extensions) {
+                return false;
+            }
+
+            var extension = property.extensions[this.name] as T;
+            if (!extension) {
+                return false;
+            }
+
+            // Clear out the extension before executing the action to avoid recursing into the same property.
+            property.extensions[this.name] = undefined;
+
+            action(extension, () => {
+                // Restore the extension after completing the action.
+                property.extensions[this.name] = extension;
+            });
+
+            return true;
+        }
 
         //
         // Utilities
@@ -14,8 +39,16 @@ module BABYLON.GLTF2 {
 
         public static _Extensions: GLTFLoaderExtension[] = [];
 
+        public static TraverseNode(loader: GLTFLoader, index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean {
+            return this._ApplyExtensions(extension => extension._traverseNode(loader, index, action, parentNode));
+        }
+
+        public static LoadNode(loader: GLTFLoader, node: IGLTFNode): boolean {
+            return this._ApplyExtensions(extension => extension._loadNode(loader, node));
+        }
+
         public static LoadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean {
-            return this._ApplyExtensions(extension => extension.loadMaterial(loader, material, assign));
+            return this._ApplyExtensions(extension => extension._loadMaterial(loader, material, assign));
         }
 
         private static _ApplyExtensions(action: (extension: GLTFLoaderExtension) => boolean) {

+ 11 - 4
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -38,18 +38,25 @@ module BABYLON {
         public coordinateSystemMode: GLTFLoaderCoordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
         public onTextureLoaded: (texture: BaseTexture) => void;
         public onMaterialLoaded: (material: Material) => void;
+
         /**
          * Let the user decides if he needs to process the material (like precompilation) before affecting it to meshes
          */
         public onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
+
         /**
-         * Raised when all LODs are complete (or if there is no LOD and model is complete)
+         * Raised when the visible components (geometry, materials, textures, etc.) are first ready to be rendered.
+         * For assets with LODs, raised when the first LOD is complete.
+         * For assets without LODs, raised when the model is complete just before onComplete.
          */
-        public onComplete: () => void;
+        public onReady: () => void;
+
         /**
-         * Raised when first LOD complete (or if there is no LOD and model is complete)
+         * Raised when the asset is completely loaded, just 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 just after onReady.
          */
-        public onFirstLODComplete: () => void;
+        public onComplete: () => void;
 
         public name = "gltf";
 

+ 2 - 2
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -135,9 +135,9 @@
                         }
                     }
                     currentRotation.multiplyInPlace(mesh.rotationQuaternion);
-                    mesh.getChildMeshes(true).forEach(processMesh.bind(this, mesh.getAbsolutePosition()));
+                    mesh.getChildMeshes(true).filter(m => !!m.physicsImpostor).forEach(processMesh.bind(this, mesh.getAbsolutePosition()));
                 }
-                meshChildren.forEach(processMesh.bind(this, mainImpostor.object.getAbsolutePosition()));
+                meshChildren.filter(m => !!m.physicsImpostor).forEach(processMesh.bind(this, mainImpostor.object.getAbsolutePosition()));
             }
         }
 

+ 7 - 1
src/PostProcess/babylon.volumetricLightScatteringPostProcess.ts

@@ -252,7 +252,13 @@
             this._volumetricLightScatteringRTT.wrapV = Texture.CLAMP_ADDRESSMODE;
             this._volumetricLightScatteringRTT.renderList = null;
             this._volumetricLightScatteringRTT.renderParticles = false;
-            scene.customRenderTargets.push(this._volumetricLightScatteringRTT);
+
+            var camera = this.getCamera();
+            if (camera) {
+                camera.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            } else {
+                scene.customRenderTargets.push(this._volumetricLightScatteringRTT);
+            }
 
             // Custom render function for submeshes
             var renderSubMesh = (subMesh: SubMesh): void => {