David Catuhe 7 年之前
父節點
當前提交
026af3caf0

File diff suppressed because it is too large
+ 6853 - 6851
Playground/babylon.d.txt


File diff suppressed because it is too large
+ 8563 - 8560
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 7 - 7
dist/preview release/babylon.js


+ 23 - 7
dist/preview release/babylon.max.js

@@ -87843,21 +87843,31 @@ var BABYLON;
                     var pointerEvent = (prePointerInfo.event);
                     // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
                     if (originalScenePick && utilityScenePick) {
+                        // No pick in utility scene
                         if (utilityScenePick.distance === 0) {
-                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                // We touched an utility mesh present in the main scene
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
                         }
+                        // We pick something in utility scene or the pick in utility is closer than the one in main scene
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            if (!prePointerInfo.skipOnPointerObservable) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
-                            }
+                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
                             prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                            // We have a pick in both scenes but main is closer than utility
+                            // We touched an utility mesh present in the main scene
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
                                 _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
                                 delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
@@ -87880,6 +87890,12 @@ var BABYLON;
             });
             this._updateCamera();
         }
+        UtilityLayerRenderer.prototype._notifyObservers = function (prePointerInfo, pickInfo, pointerEvent) {
+            if (!prePointerInfo.skipOnPointerObservable) {
+                this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
+                this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
+            }
+        };
         /**
          * Renders the utility layers scene on top of the original scene
          */

+ 23 - 7
dist/preview release/babylon.no-module.max.js

@@ -87810,21 +87810,31 @@ var BABYLON;
                     var pointerEvent = (prePointerInfo.event);
                     // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
                     if (originalScenePick && utilityScenePick) {
+                        // No pick in utility scene
                         if (utilityScenePick.distance === 0) {
-                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                // We touched an utility mesh present in the main scene
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
                         }
+                        // We pick something in utility scene or the pick in utility is closer than the one in main scene
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            if (!prePointerInfo.skipOnPointerObservable) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
-                            }
+                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
                             prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                            // We have a pick in both scenes but main is closer than utility
+                            // We touched an utility mesh present in the main scene
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
                                 _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
                                 delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
@@ -87847,6 +87857,12 @@ var BABYLON;
             });
             this._updateCamera();
         }
+        UtilityLayerRenderer.prototype._notifyObservers = function (prePointerInfo, pickInfo, pointerEvent) {
+            if (!prePointerInfo.skipOnPointerObservable) {
+                this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
+                this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
+            }
+        };
         /**
          * Renders the utility layers scene on top of the original scene
          */

File diff suppressed because it is too large
+ 7 - 7
dist/preview release/babylon.worker.js


+ 23 - 7
dist/preview release/es6.js

@@ -87810,21 +87810,31 @@ var BABYLON;
                     var pointerEvent = (prePointerInfo.event);
                     // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
                     if (originalScenePick && utilityScenePick) {
+                        // No pick in utility scene
                         if (utilityScenePick.distance === 0) {
-                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                // We touched an utility mesh present in the main scene
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
                         }
+                        // We pick something in utility scene or the pick in utility is closer than the one in main scene
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            if (!prePointerInfo.skipOnPointerObservable) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
-                            }
+                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
                             prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                            // We have a pick in both scenes but main is closer than utility
+                            // We touched an utility mesh present in the main scene
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
                                 _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
                                 delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
@@ -87847,6 +87857,12 @@ var BABYLON;
             });
             this._updateCamera();
         }
+        UtilityLayerRenderer.prototype._notifyObservers = function (prePointerInfo, pickInfo, pointerEvent) {
+            if (!prePointerInfo.skipOnPointerObservable) {
+                this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
+                this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
+            }
+        };
         /**
          * Renders the utility layers scene on top of the original scene
          */

+ 3 - 0
dist/preview release/gui/babylon.gui.js

@@ -6827,6 +6827,9 @@ var BABYLON;
                 });
                 this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene);
                 this._utilityLayer.onlyCheckPointerDownEvents = false;
+                this._utilityLayer.mainSceneTrackerPredicate = function (mesh) {
+                    return mesh && mesh.metadata && mesh.metadata._node;
+                };
                 // Root
                 this._rootContainer = new GUI.Container3D("RootContainer");
                 this._rootContainer._host = this;

File diff suppressed because it is too large
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


File diff suppressed because it is too large
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


File diff suppressed because it is too large
+ 12 - 12
dist/preview release/viewer/babylon.viewer.js


+ 206 - 143
dist/preview release/viewer/babylon.viewer.max.js

@@ -87931,21 +87931,31 @@ var BABYLON;
                     var pointerEvent = (prePointerInfo.event);
                     // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
                     if (originalScenePick && utilityScenePick) {
+                        // No pick in utility scene
                         if (utilityScenePick.distance === 0) {
-                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                // We touched an utility mesh present in the main scene
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
                         }
+                        // We pick something in utility scene or the pick in utility is closer than the one in main scene
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            if (!prePointerInfo.skipOnPointerObservable) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
-                            }
+                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
                             prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                            // We have a pick in both scenes but main is closer than utility
+                            // We touched an utility mesh present in the main scene
+                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            }
+                            else if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
                                 _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
                                 delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
@@ -87968,6 +87978,12 @@ var BABYLON;
             });
             this._updateCamera();
         }
+        UtilityLayerRenderer.prototype._notifyObservers = function (prePointerInfo, pickInfo, pointerEvent) {
+            if (!prePointerInfo.skipOnPointerObservable) {
+                this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
+                this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
+            }
+        };
         /**
          * Renders the utility layers scene on top of the original scene
          */
@@ -104413,23 +104429,26 @@ var BABYLON;
         }
         STLFileLoader.prototype.importMesh = function (meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons) {
             var matches;
-            if (this.isBinary(data)) {
-                // binary .stl
-                var babylonMesh = new BABYLON.Mesh("stlmesh", scene);
-                this.parseBinary(babylonMesh, data);
-                if (meshes) {
-                    meshes.push(babylonMesh);
+            if (typeof data !== "string") {
+                if (this.isBinary(data)) {
+                    // binary .stl
+                    var babylonMesh = new BABYLON.Mesh("stlmesh", scene);
+                    this.parseBinary(babylonMesh, data);
+                    if (meshes) {
+                        meshes.push(babylonMesh);
+                    }
+                    return true;
                 }
-                return true;
-            }
-            // ASCII .stl
-            // convert to string
-            var array_buffer = new Uint8Array(data);
-            var str = '';
-            for (var i = 0; i < data.byteLength; i++) {
-                str += String.fromCharCode(array_buffer[i]); // implicitly assumes little-endian
+                // ASCII .stl
+                // convert to string
+                var array_buffer = new Uint8Array(data);
+                var str = '';
+                for (var i = 0; i < data.byteLength; i++) {
+                    str += String.fromCharCode(array_buffer[i]); // implicitly assumes little-endian
+                }
+                data = str;
             }
-            data = str;
+            //if arrived here, data is a string, containing the STLA data.
             while (matches = this.solidPattern.exec(data)) {
                 var meshName = matches[1];
                 var meshNameFromEnd = matches[3];
@@ -105464,15 +105483,6 @@ var BABYLON;
             /** @hidden */
             this._normalizeAnimationGroupsToBeginAtZero = true;
             /**
-             * Defines if the loader logging is enabled.
-             */
-            this.loggingEnabled = false;
-            /**
-             * Observable raised when the loader logs a message.
-             */
-            this.onLogObservable = new BABYLON.Observable();
-            this._logIndentLevel = 0;
-            /**
              * Function called before loading a url referenced by the asset.
              */
             this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };
@@ -105520,6 +105530,15 @@ var BABYLON;
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
             };
+            this._logIndentLevel = 0;
+            this._loggingEnabled = false;
+            /** @hidden */
+            this._log = this._logDisabled;
+            this._capturePerformanceCounters = false;
+            /** @hidden */
+            this._startPerformanceCounter = this._startPerformanceCounterDisabled;
+            /** @hidden */
+            this._endPerformanceCounter = this._endPerformanceCounterDisabled;
         }
         Object.defineProperty(GLTFFileLoader.prototype, "onParsed", {
             /**
@@ -105534,23 +105553,6 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        /** @hidden */
-        GLTFFileLoader.prototype._log = function (message) {
-            if (this.loggingEnabled) {
-                var spaces = GLTFFileLoader._logSpaces.substr(0, this._logIndentLevel * 2);
-                this.onLogObservable.notifyObservers("" + spaces + message);
-                BABYLON.Tools.Log("" + spaces + message);
-            }
-        };
-        /** @hidden */
-        GLTFFileLoader.prototype._logOpen = function (message) {
-            this._log(message);
-            this._logIndentLevel++;
-        };
-        /** @hidden */
-        GLTFFileLoader.prototype._logClose = function () {
-            --this._logIndentLevel;
-        };
         Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
             /**
              * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
@@ -105664,6 +105666,52 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "loggingEnabled", {
+            /**
+             * Defines if the loader logging is enabled.
+             */
+            get: function () {
+                return this._loggingEnabled;
+            },
+            set: function (value) {
+                if (this._loggingEnabled === value) {
+                    return;
+                }
+                this._loggingEnabled = value;
+                if (this._loggingEnabled) {
+                    this._log = this._logEnabled;
+                }
+                else {
+                    this._log = this._logDisabled;
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "capturePerformanceCounters", {
+            /**
+             * Defines if the loader should capture performance counters.
+             */
+            get: function () {
+                return this._capturePerformanceCounters;
+            },
+            set: function (value) {
+                if (this._capturePerformanceCounters === value) {
+                    return;
+                }
+                this._capturePerformanceCounters = value;
+                if (this._capturePerformanceCounters) {
+                    this._startPerformanceCounter = this._startPerformanceCounterEnabled;
+                    this._endPerformanceCounter = this._endPerformanceCounterEnabled;
+                }
+                else {
+                    this._startPerformanceCounter = this._startPerformanceCounterDisabled;
+                    this._endPerformanceCounter = this._endPerformanceCounterDisabled;
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
@@ -105759,18 +105807,15 @@ var BABYLON;
             return new GLTFFileLoader();
         };
         GLTFFileLoader.prototype._parse = function (data) {
+            this._startPerformanceCounter("Parse");
             var parsedData;
             if (data instanceof ArrayBuffer) {
-                if (this.loggingEnabled) {
-                    this._log("Parsing binary");
-                }
+                this._log("Parsing binary");
                 parsedData = this._parseBinary(data);
             }
             else {
-                if (this.loggingEnabled) {
-                    this._log("Parsing JSON");
-                    this._log("JSON length: " + data.length);
-                }
+                this._log("Parsing JSON");
+                this._log("JSON length: " + data.length);
                 parsedData = {
                     json: JSON.parse(data),
                     bin: null
@@ -105778,19 +105823,14 @@ var BABYLON;
             }
             this.onParsedObservable.notifyObservers(parsedData);
             this.onParsedObservable.clear();
+            this._endPerformanceCounter("Parse");
             return parsedData;
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
             var asset = loaderData.json.asset || {};
-            if (this.loggingEnabled) {
-                this._log("Asset version: " + asset.version);
-                if (asset.minVersion) {
-                    this._log("Asset minimum version: " + asset.minVersion);
-                }
-                if (asset.generator) {
-                    this._log("Asset generator: " + asset.generator);
-                }
-            }
+            this._log("Asset version: " + asset.version);
+            asset.minVersion && this._log("Asset minimum version: " + asset.minVersion);
+            asset.generator && this._log("Asset generator: " + asset.generator);
             var version = GLTFFileLoader._parseVersion(asset.version);
             if (!version) {
                 throw new Error("Invalid version: " + asset.version);
@@ -105818,9 +105858,7 @@ var BABYLON;
             var Binary = {
                 Magic: 0x46546C67
             };
-            if (this.loggingEnabled) {
-                this._log("Binary length: " + data.byteLength);
-            }
+            this._log("Binary length: " + data.byteLength);
             var binaryReader = new BinaryReader(data);
             var magic = binaryReader.readUint32();
             if (magic !== Binary.Magic) {
@@ -105939,6 +105977,31 @@ var BABYLON;
             }
             return result;
         };
+        /** @hidden */
+        GLTFFileLoader.prototype._logOpen = function (message) {
+            this._log(message);
+            this._logIndentLevel++;
+        };
+        /** @hidden */
+        GLTFFileLoader.prototype._logClose = function () {
+            --this._logIndentLevel;
+        };
+        GLTFFileLoader.prototype._logEnabled = function (message) {
+            var spaces = GLTFFileLoader._logSpaces.substr(0, this._logIndentLevel * 2);
+            BABYLON.Tools.Log("" + spaces + message);
+        };
+        GLTFFileLoader.prototype._logDisabled = function (message) {
+        };
+        GLTFFileLoader.prototype._startPerformanceCounterEnabled = function (counterName) {
+            BABYLON.Tools.StartPerformanceCounter(counterName);
+        };
+        GLTFFileLoader.prototype._startPerformanceCounterDisabled = function (counterName) {
+        };
+        GLTFFileLoader.prototype._endPerformanceCounterEnabled = function (counterName) {
+            BABYLON.Tools.EndPerformanceCounter(counterName);
+        };
+        GLTFFileLoader.prototype._endPerformanceCounterDisabled = function (counterName) {
+        };
         // #endregion
         // #region V1 options
         /**
@@ -108204,7 +108267,6 @@ var BABYLON;
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
                 this._completePromises = new Array();
-                this._onReadyObservable = new BABYLON.Observable();
                 this._disposed = false;
                 this._state = null;
                 this._extensions = {};
@@ -108244,8 +108306,8 @@ var BABYLON;
                 this._requests.length = 0;
                 delete this._gltf;
                 delete this._babylonScene;
+                delete this._readyPromise;
                 this._completePromises.length = 0;
-                this._onReadyObservable.clear();
                 for (var name_1 in this._extensions) {
                     this._extensions[name_1].dispose();
                 }
@@ -108268,14 +108330,14 @@ var BABYLON;
                             for (var _i = 0, _a = _this._gltf.nodes; _i < _a.length; _i++) {
                                 var node = _a[_i];
                                 if (node.name) {
-                                    nodeMap_1[node.name] = node;
+                                    nodeMap_1[node.name] = node._index;
                                 }
                             }
                         }
                         var names = (meshesNames instanceof Array) ? meshesNames : [meshesNames];
                         nodes = names.map(function (name) {
                             var node = nodeMap_1[name];
-                            if (!node) {
+                            if (node === undefined) {
                                 throw new Error("Failed to find node '" + name + "'");
                             }
                             return node;
@@ -108304,13 +108366,17 @@ var BABYLON;
             GLTFLoader.prototype._loadAsync = function (nodes) {
                 var _this = this;
                 return Promise.resolve().then(function () {
+                    _this._parent._startPerformanceCounter("Loading => Ready");
+                    _this._parent._startPerformanceCounter("Loading => Complete");
                     _this._state = BABYLON.GLTFLoaderState.LOADING;
                     _this._parent._log("Loading");
+                    var readyDeferred = new BABYLON.Deferred();
+                    _this._readyPromise = readyDeferred.promise;
                     _this._loadExtensions();
                     _this._checkExtensions();
                     var promises = new Array();
                     if (nodes) {
-                        promises.push(_this._loadNodesAsync(nodes));
+                        promises.push(_this._loadSceneAsync("#/nodes", { nodes: nodes, _index: -1 }));
                     }
                     else {
                         var scene = GLTFLoader._GetProperty("#/scene", _this._gltf.scenes, _this._gltf.scene || 0);
@@ -108325,16 +108391,18 @@ var BABYLON;
                     var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.READY;
                         _this._parent._log("Ready");
-                        _this._onReadyObservable.notifyObservers(_this);
+                        readyDeferred.resolve();
                         _this._startAnimations();
                     });
                     resultPromise.then(function () {
+                        _this._parent._endPerformanceCounter("Loading => Ready");
                         if (_this._rootBabylonMesh) {
                             _this._rootBabylonMesh.setEnabled(true);
                         }
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
+                                    _this._parent._endPerformanceCounter("Loading => Complete");
                                     _this._state = BABYLON.GLTFLoaderState.COMPLETE;
                                     _this._parent._log("Complete");
                                     _this._parent.onCompleteObservable.notifyObservers(undefined);
@@ -108450,15 +108518,6 @@ var BABYLON;
                 this._parent.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
                 return rootNode;
             };
-            GLTFLoader.prototype._loadNodesAsync = function (nodes) {
-                var promises = new Array();
-                for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
-                    var node = nodes_1[_i];
-                    promises.push(this._loadNodeAsync("#/nodes/" + node._index, node));
-                }
-                promises.push(this._loadAnimationsAsync());
-                return Promise.all(promises).then(function () { });
-            };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
                 var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
                 if (promise) {
@@ -108494,8 +108553,8 @@ var BABYLON;
                 meshes.push(this._rootBabylonMesh);
                 var nodes = this._gltf.nodes;
                 if (nodes) {
-                    for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
-                        var node = nodes_2[_i];
+                    for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
+                        var node = nodes_1[_i];
                         if (node._babylonMesh) {
                             meshes.push(node._babylonMesh);
                         }
@@ -109613,6 +109672,8 @@ var BABYLON;
                 throw new Error(context + ": Invalid mesh primitive mode (" + mode + ")");
             };
             GLTFLoader.prototype._compileMaterialsAsync = function () {
+                var _this = this;
+                this._parent._startPerformanceCounter("Compile materials");
                 var promises = new Array();
                 if (this._gltf.materials) {
                     for (var _i = 0, _a = this._gltf.materials; _i < _a.length; _i++) {
@@ -109634,9 +109695,13 @@ var BABYLON;
                         }
                     }
                 }
-                return Promise.all(promises).then(function () { });
+                return Promise.all(promises).then(function () {
+                    _this._parent._endPerformanceCounter("Compile materials");
+                });
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function () {
+                var _this = this;
+                this._parent._startPerformanceCounter("Compile shadow generators");
                 var promises = new Array();
                 var lights = this._babylonScene.lights;
                 for (var _i = 0, lights_1 = lights; _i < lights_1.length; _i++) {
@@ -109646,7 +109711,9 @@ var BABYLON;
                         promises.push(generator.forceCompilationAsync());
                     }
                 }
-                return Promise.all(promises).then(function () { });
+                return Promise.all(promises).then(function () {
+                    _this._parent._endPerformanceCounter("Compile shadow generators");
+                });
             };
             GLTFLoader.prototype._applyExtensions = function (actionAsync) {
                 for (var _i = 0, _a = GLTFLoader._ExtensionNames; _i < _a.length; _i++) {
@@ -109859,29 +109926,45 @@ var BABYLON;
                      * Dispose the loader to cancel the loading of the next level of LODs.
                      */
                     _this.onMaterialLODsLoadedObservable = new BABYLON.Observable();
-                    _this._loadingNodeLOD = null;
-                    _this._loadNodeSignals = {};
-                    _this._loadNodePromises = new Array();
-                    _this._loadingMaterialLOD = null;
-                    _this._loadMaterialSignals = {};
-                    _this._loadMaterialPromises = new Array();
-                    _this._loader._onReadyObservable.addOnce(function () {
+                    _this._nodeIndexLOD = null;
+                    _this._nodeSignalLODs = new Array();
+                    _this._nodePromiseLODs = new Array();
+                    _this._materialIndexLOD = null;
+                    _this._materialSignalLODs = new Array();
+                    _this._materialPromiseLODs = new Array();
+                    _this._loader._readyPromise.then(function () {
                         var _loop_1 = function (indexLOD) {
-                            Promise.all(_this._loadNodePromises[indexLOD]).then(function () {
+                            var promise = Promise.all(_this._nodePromiseLODs[indexLOD]).then(function () {
+                                if (indexLOD !== 0) {
+                                    _this._loader._parent._endPerformanceCounter("Node LOD " + indexLOD);
+                                }
                                 _this._loader._parent._log("Loaded node LOD " + indexLOD);
                                 _this.onNodeLODsLoadedObservable.notifyObservers(indexLOD);
+                                if (indexLOD !== _this._nodePromiseLODs.length - 1) {
+                                    _this._loader._parent._startPerformanceCounter("Node LOD " + (indexLOD + 1));
+                                    _this._nodeSignalLODs[indexLOD].resolve();
+                                }
                             });
+                            _this._loader._completePromises.push(promise);
                         };
-                        for (var indexLOD = 0; indexLOD < _this._loadNodePromises.length; indexLOD++) {
+                        for (var indexLOD = 0; indexLOD < _this._nodePromiseLODs.length; indexLOD++) {
                             _loop_1(indexLOD);
                         }
                         var _loop_2 = function (indexLOD) {
-                            Promise.all(_this._loadMaterialPromises[indexLOD]).then(function () {
+                            var promise = Promise.all(_this._materialPromiseLODs[indexLOD]).then(function () {
+                                if (indexLOD !== 0) {
+                                    _this._loader._parent._endPerformanceCounter("Material LOD " + indexLOD);
+                                }
                                 _this._loader._parent._log("Loaded material LOD " + indexLOD);
                                 _this.onMaterialLODsLoadedObservable.notifyObservers(indexLOD);
+                                if (indexLOD !== _this._materialPromiseLODs.length - 1) {
+                                    _this._loader._parent._startPerformanceCounter("Material LOD " + (indexLOD + 1));
+                                    _this._materialSignalLODs[indexLOD].resolve();
+                                }
                             });
+                            _this._loader._completePromises.push(promise);
                         };
-                        for (var indexLOD = 0; indexLOD < _this._loadMaterialPromises.length; indexLOD++) {
+                        for (var indexLOD = 0; indexLOD < _this._materialPromiseLODs.length; indexLOD++) {
                             _loop_2(indexLOD);
                         }
                     });
@@ -109889,10 +109972,12 @@ var BABYLON;
                 }
                 MSFT_lod.prototype.dispose = function () {
                     _super.prototype.dispose.call(this);
-                    this._loadingNodeLOD = null;
-                    this._loadNodeSignals = {};
-                    this._loadingMaterialLOD = null;
-                    this._loadMaterialSignals = {};
+                    this._nodeIndexLOD = null;
+                    this._nodeSignalLODs.length = 0;
+                    this._nodePromiseLODs.length = 0;
+                    this._materialIndexLOD = null;
+                    this._materialSignalLODs.length = 0;
+                    this._materialPromiseLODs.length = 0;
                     this.onMaterialLODsLoadedObservable.clear();
                     this.onNodeLODsLoadedObservable.clear();
                 };
@@ -109905,10 +109990,8 @@ var BABYLON;
                         var _loop_3 = function (indexLOD) {
                             var nodeLOD = nodeLODs[indexLOD];
                             if (indexLOD !== 0) {
-                                _this._loadingNodeLOD = nodeLOD;
-                                if (!_this._loadNodeSignals[nodeLOD._index]) {
-                                    _this._loadNodeSignals[nodeLOD._index] = new BABYLON.Deferred();
-                                }
+                                _this._nodeIndexLOD = indexLOD;
+                                _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                             }
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                                 if (indexLOD !== 0) {
@@ -109918,23 +110001,15 @@ var BABYLON;
                                         delete previousNodeLOD._babylonMesh;
                                     }
                                 }
-                                if (indexLOD !== nodeLODs.length - 1) {
-                                    var nodeIndex = nodeLODs[indexLOD + 1]._index;
-                                    if (_this._loadNodeSignals[nodeIndex]) {
-                                        _this._loadNodeSignals[nodeIndex].resolve();
-                                        delete _this._loadNodeSignals[nodeIndex];
-                                    }
-                                }
                             });
                             if (indexLOD === 0) {
                                 firstPromise = promise;
                             }
                             else {
-                                _this._loader._completePromises.push(promise);
-                                _this._loadingNodeLOD = null;
+                                _this._nodeIndexLOD = null;
                             }
-                            _this._loadNodePromises[indexLOD] = _this._loadNodePromises[indexLOD] || [];
-                            _this._loadNodePromises[indexLOD].push(promise);
+                            _this._nodePromiseLODs[indexLOD] = _this._nodePromiseLODs[indexLOD] || [];
+                            _this._nodePromiseLODs[indexLOD].push(promise);
                         };
                         for (var indexLOD = 0; indexLOD < nodeLODs.length; indexLOD++) {
                             _loop_3(indexLOD);
@@ -109946,7 +110021,7 @@ var BABYLON;
                 MSFT_lod.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
-                    if (this._loadingNodeLOD) {
+                    if (this._nodeIndexLOD) {
                         return null;
                     }
                     return this._loadExtensionAsync(context, material, function (extensionContext, extension) {
@@ -109956,10 +110031,7 @@ var BABYLON;
                         var _loop_4 = function (indexLOD) {
                             var materialLOD = materialLODs[indexLOD];
                             if (indexLOD !== 0) {
-                                _this._loadingMaterialLOD = materialLOD;
-                                if (!_this._loadMaterialSignals[materialLOD._index]) {
-                                    _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
-                                }
+                                _this._materialIndexLOD = indexLOD;
                             }
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, mesh, babylonMesh, babylonDrawMode, indexLOD === 0 ? assign : function () { }).then(function () {
                                 if (indexLOD !== 0) {
@@ -109971,23 +110043,15 @@ var BABYLON;
                                         delete previousBabylonDataLOD[babylonDrawMode];
                                     }
                                 }
-                                if (indexLOD !== materialLODs.length - 1) {
-                                    var materialIndex = materialLODs[indexLOD + 1]._index;
-                                    if (_this._loadMaterialSignals[materialIndex]) {
-                                        _this._loadMaterialSignals[materialIndex].resolve();
-                                        delete _this._loadMaterialSignals[materialIndex];
-                                    }
-                                }
                             });
                             if (indexLOD === 0) {
                                 firstPromise = promise;
                             }
                             else {
-                                _this._loader._completePromises.push(promise);
-                                _this._loadingMaterialLOD = null;
+                                _this._materialIndexLOD = null;
                             }
-                            _this._loadMaterialPromises[indexLOD] = _this._loadMaterialPromises[indexLOD] || [];
-                            _this._loadMaterialPromises[indexLOD].push(promise);
+                            _this._materialPromiseLODs[indexLOD] = _this._materialPromiseLODs[indexLOD] || [];
+                            _this._materialPromiseLODs[indexLOD].push(promise);
                         };
                         for (var indexLOD = 0; indexLOD < materialLODs.length; indexLOD++) {
                             _loop_4(indexLOD);
@@ -109998,21 +110062,20 @@ var BABYLON;
                 };
                 MSFT_lod.prototype._loadUriAsync = function (context, uri) {
                     var _this = this;
-                    if (this._loadingMaterialLOD || this._loadingNodeLOD) {
-                        if (this._loader._parent.loggingEnabled) {
-                            this._loader._parent._log("deferred");
-                        }
-                    }
                     // Defer the loading of uris if loading a material or node LOD.
-                    if (this._loadingMaterialLOD) {
-                        var index = this._loadingMaterialLOD._index;
-                        return this._loadMaterialSignals[index].promise.then(function () {
+                    if (this._materialIndexLOD !== null) {
+                        this._loader._parent._log("deferred");
+                        var previousIndexLOD = this._materialIndexLOD - 1;
+                        this._materialSignalLODs[previousIndexLOD] = this._materialSignalLODs[previousIndexLOD] || new BABYLON.Deferred();
+                        return this._materialSignalLODs[previousIndexLOD].promise.then(function () {
                             return _this._loader._loadUriAsync(context, uri);
                         });
                     }
-                    else if (this._loadingNodeLOD) {
-                        var index = this._loadingNodeLOD._index;
-                        return this._loadNodeSignals[index].promise.then(function () {
+                    else if (this._nodeIndexLOD !== null) {
+                        this._loader._parent._log("deferred");
+                        var previousIndexLOD = this._nodeIndexLOD - 1;
+                        this._nodeSignalLODs[previousIndexLOD] = this._nodeSignalLODs[previousIndexLOD] || new BABYLON.Deferred();
+                        return this._nodeSignalLODs[this._nodeIndexLOD - 1].promise.then(function () {
                             return _this._loader._loadUriAsync(context, uri);
                         });
                     }

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

@@ -53,6 +53,9 @@ module BABYLON.GUI {
 
             this._utilityLayer = new UtilityLayerRenderer(this._scene);
             this._utilityLayer.onlyCheckPointerDownEvents = false;
+            this._utilityLayer.mainSceneTrackerPredicate = (mesh: Nullable<AbstractMesh>) => {
+                return mesh && mesh.metadata && mesh.metadata._node;
+            }
 
             // Root
             this._rootContainer = new Container3D("RootContainer");

+ 27 - 8
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -30,6 +30,9 @@ module BABYLON {
          */
         public onPointerOutObservable = new Observable<number>();
 
+        /** Gets or sets a predicate that will be used to indicate utility meshes present in the main scene */
+        public mainSceneTrackerPredicate: (mesh: Nullable<AbstractMesh>) => boolean;
+
         private _afterRenderObserver:Nullable<Observer<Scene>>;
         private _sceneDisposeObserver:Nullable<Observer<Scene>>;
         private _originalPointerObserver:Nullable<Observer<PointerInfoPre>>;
@@ -88,22 +91,31 @@ module BABYLON {
                     let pointerEvent = <PointerEvent>(prePointerInfo.event);
 
                     // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
-                    if(originalScenePick && utilityScenePick){
+                    if (originalScenePick && utilityScenePick){
+                        // No pick in utility scene
                         if (utilityScenePick.distance === 0) {
-                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                            if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                // We touched an utility mesh present in the main scene
+                                this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;                             
+                            } else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 this._pointerCaptures[pointerEvent.pointerId] = true;
                             } 
                         }
 
+                        // We pick something in utility scene or the pick in utility is closer than the one in main scene
                         if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)){
-                            if(!prePointerInfo.skipOnPointerObservable){
-                                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
-                                this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
-                            }
+                            this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
                             prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                         } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (this._lastPointerEvents[pointerEvent.pointerId]) {
+                            // We have a pick in both scenes but main is closer than utility
+
+                            // We touched an utility mesh present in the main scene
+                            if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;                             
+                            } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
                                 this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
                                 delete this._lastPointerEvents[pointerEvent.pointerId];
                             }
@@ -133,6 +145,13 @@ module BABYLON {
             this._updateCamera();
         }
 
+        private _notifyObservers(prePointerInfo: PointerInfoPre, pickInfo: PickingInfo, pointerEvent: PointerEvent) {
+            if (!prePointerInfo.skipOnPointerObservable){
+                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo))
+                this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
+            }
+        }
+
         /**
          * Renders the utility layers scene on top of the original scene
          */