Procházet zdrojové kódy

Better physics viewer support

David Catuhe před 6 roky
rodič
revize
95a4cf1a69

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 6573 - 6571
Playground/babylon.d.txt


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 7549 - 7547
dist/preview release/babylon.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/babylon.js


+ 106 - 88
dist/preview release/babylon.max.js

@@ -67005,6 +67005,9 @@ var BABYLON;
                 if (physicEngine) {
                 if (physicEngine) {
                     this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                     this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                 }
                 }
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene, false);
+                this._utilityLayer.pickUtilitySceneFirst = false;
+                this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
             }
             }
             /** @hidden */
             /** @hidden */
             PhysicsViewer.prototype._updateDebugMeshes = function () {
             PhysicsViewer.prototype._updateDebugMeshes = function () {
@@ -67039,7 +67042,7 @@ var BABYLON;
                         return null;
                         return null;
                     }
                     }
                 }
                 }
-                var debugMesh = this._getDebugMesh(impostor, this._scene);
+                var debugMesh = this._getDebugMesh(impostor);
                 if (debugMesh) {
                 if (debugMesh) {
                     this._impostors[this._numMeshes] = impostor;
                     this._impostors[this._numMeshes] = impostor;
                     this._meshes[this._numMeshes] = debugMesh;
                     this._meshes[this._numMeshes] = debugMesh;
@@ -67056,17 +67059,18 @@ var BABYLON;
              * @param impostor defines the impostor to hide
              * @param impostor defines the impostor to hide
              */
              */
             PhysicsViewer.prototype.hideImpostor = function (impostor) {
             PhysicsViewer.prototype.hideImpostor = function (impostor) {
-                if (!impostor || !this._scene) {
+                if (!impostor || !this._scene || !this._utilityLayer) {
                     return;
                     return;
                 }
                 }
                 var removed = false;
                 var removed = false;
+                var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 for (var i = 0; i < this._numMeshes; i++) {
                 for (var i = 0; i < this._numMeshes; i++) {
                     if (this._impostors[i] == impostor) {
                     if (this._impostors[i] == impostor) {
                         var mesh = this._meshes[i];
                         var mesh = this._meshes[i];
                         if (!mesh) {
                         if (!mesh) {
                             continue;
                             continue;
                         }
                         }
-                        this._scene.removeMesh(mesh);
+                        utilityLayerScene.removeMesh(mesh);
                         mesh.dispose();
                         mesh.dispose();
                         this._numMeshes--;
                         this._numMeshes--;
                         if (this._numMeshes > 0) {
                         if (this._numMeshes > 0) {
@@ -67091,37 +67095,39 @@ var BABYLON;
                 if (!this._debugMaterial) {
                 if (!this._debugMaterial) {
                     this._debugMaterial = new BABYLON.StandardMaterial('', scene);
                     this._debugMaterial = new BABYLON.StandardMaterial('', scene);
                     this._debugMaterial.wireframe = true;
                     this._debugMaterial.wireframe = true;
+                    this._debugMaterial.emissiveColor = BABYLON.Color3.White();
+                    this._debugMaterial.disableLighting = true;
                 }
                 }
                 return this._debugMaterial;
                 return this._debugMaterial;
             };
             };
             PhysicsViewer.prototype._getDebugBoxMesh = function (scene) {
             PhysicsViewer.prototype._getDebugBoxMesh = function (scene) {
                 if (!this._debugBoxMesh) {
                 if (!this._debugBoxMesh) {
                     this._debugBoxMesh = BABYLON.MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
                     this._debugBoxMesh = BABYLON.MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
-                    this._debugBoxMesh.renderingGroupId = 1;
                     this._debugBoxMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugBoxMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugBoxMesh.material = this._getDebugMaterial(scene);
                     this._debugBoxMesh.material = this._getDebugMaterial(scene);
-                    scene.removeMesh(this._debugBoxMesh);
                 }
                 }
                 return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
                 return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
             };
             };
             PhysicsViewer.prototype._getDebugSphereMesh = function (scene) {
             PhysicsViewer.prototype._getDebugSphereMesh = function (scene) {
                 if (!this._debugSphereMesh) {
                 if (!this._debugSphereMesh) {
                     this._debugSphereMesh = BABYLON.MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
                     this._debugSphereMesh = BABYLON.MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
-                    this._debugSphereMesh.renderingGroupId = 1;
                     this._debugSphereMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugSphereMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugSphereMesh.material = this._getDebugMaterial(scene);
                     this._debugSphereMesh.material = this._getDebugMaterial(scene);
-                    scene.removeMesh(this._debugSphereMesh);
                 }
                 }
                 return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
                 return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
             };
             };
-            PhysicsViewer.prototype._getDebugMesh = function (impostor, scene) {
+            PhysicsViewer.prototype._getDebugMesh = function (impostor) {
+                if (!this._utilityLayer) {
+                    return null;
+                }
                 var mesh = null;
                 var mesh = null;
+                var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 if (impostor.type == BABYLON.PhysicsImpostor.BoxImpostor) {
                 if (impostor.type == BABYLON.PhysicsImpostor.BoxImpostor) {
-                    mesh = this._getDebugBoxMesh(scene);
+                    mesh = this._getDebugBoxMesh(utilityLayerScene);
                     impostor.getBoxSizeToRef(mesh.scaling);
                     impostor.getBoxSizeToRef(mesh.scaling);
                 }
                 }
                 else if (impostor.type == BABYLON.PhysicsImpostor.SphereImpostor) {
                 else if (impostor.type == BABYLON.PhysicsImpostor.SphereImpostor) {
-                    mesh = this._getDebugSphereMesh(scene);
+                    mesh = this._getDebugSphereMesh(utilityLayerScene);
                     var radius = impostor.getRadius();
                     var radius = impostor.getRadius();
                     mesh.scaling.x = radius * 2;
                     mesh.scaling.x = radius * 2;
                     mesh.scaling.y = radius * 2;
                     mesh.scaling.y = radius * 2;
@@ -67146,6 +67152,10 @@ var BABYLON;
                 this._impostors.length = 0;
                 this._impostors.length = 0;
                 this._scene = null;
                 this._scene = null;
                 this._physicsEnginePlugin = null;
                 this._physicsEnginePlugin = null;
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                    this._utilityLayer = null;
+                }
             };
             };
             return PhysicsViewer;
             return PhysicsViewer;
         }());
         }());
@@ -79883,9 +79893,11 @@ var BABYLON;
                 }
                 }
                 // Environment texture
                 // Environment texture
                 if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
                 if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
+                    // PBR needed for both HDR texture (gamma space) & a sky box
+                    var isPBR = parsedData.isPBR !== undefined ? parsedData.isPBR : true;
                     if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
                     if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
                         var hdrSize = (parsedData.environmentTextureSize) ? parsedData.environmentTextureSize : 128;
                         var hdrSize = (parsedData.environmentTextureSize) ? parsedData.environmentTextureSize : 128;
-                        var hdrTexture = new BABYLON.HDRCubeTexture(rootUrl + parsedData.environmentTexture, scene, hdrSize);
+                        var hdrTexture = new BABYLON.HDRCubeTexture(rootUrl + parsedData.environmentTexture, scene, hdrSize, true, !isPBR);
                         if (parsedData.environmentTextureRotationY) {
                         if (parsedData.environmentTextureRotationY) {
                             hdrTexture.rotationY = parsedData.environmentTextureRotationY;
                             hdrTexture.rotationY = parsedData.environmentTextureRotationY;
                         }
                         }
@@ -79901,8 +79913,7 @@ var BABYLON;
                     if (parsedData.createDefaultSkybox === true) {
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
-                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
+                        scene.createDefaultSkybox(scene.environmentTexture, isPBR, skyboxScale, skyboxBlurLevel);
                     }
                     }
                 }
                 }
                 // Finish
                 // Finish
@@ -98926,6 +98937,7 @@ var BABYLON;
                 this._originDirection = this._origin.subtract(this._originTop).normalize();
                 this._originDirection = this._origin.subtract(this._originTop).normalize();
             }
             }
             this._tickCallback = this._tick.bind(this);
             this._tickCallback = this._tick.bind(this);
+            this._prepareCylinder();
         }
         }
         /**
         /**
          * Returns the data related to the updraft event (cylinder).
          * Returns the data related to the updraft event (cylinder).
@@ -98957,6 +98969,9 @@ var BABYLON;
         PhysicsUpdraftEvent.prototype.dispose = function (force) {
         PhysicsUpdraftEvent.prototype.dispose = function (force) {
             var _this = this;
             var _this = this;
             if (force === void 0) { force = true; }
             if (force === void 0) { force = true; }
+            if (!this._cylinder) {
+                return;
+            }
             if (force) {
             if (force) {
                 this._cylinder.dispose();
                 this._cylinder.dispose();
             }
             }
@@ -99008,7 +99023,6 @@ var BABYLON;
         };
         };
         PhysicsUpdraftEvent.prototype._intersectsWithCylinder = function (impostor) {
         PhysicsUpdraftEvent.prototype._intersectsWithCylinder = function (impostor) {
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
-            this._prepareCylinder();
             this._cylinder.position = this._cylinderPosition;
             this._cylinder.position = this._cylinderPosition;
             return this._cylinder.intersectsMesh(impostorObject, true);
             return this._cylinder.intersectsMesh(impostorObject, true);
         };
         };
@@ -99043,6 +99057,7 @@ var BABYLON;
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._tickCallback = this._tick.bind(this);
             this._tickCallback = this._tick.bind(this);
+            this._prepareCylinder();
         }
         }
         /**
         /**
          * Returns the data related to the vortex event (cylinder).
          * Returns the data related to the vortex event (cylinder).
@@ -99147,7 +99162,6 @@ var BABYLON;
         };
         };
         PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
         PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
-            this._prepareCylinder();
             this._cylinder.position = this._cylinderPosition;
             this._cylinder.position = this._cylinderPosition;
             return this._cylinder.intersectsMesh(impostorObject, true);
             return this._cylinder.intersectsMesh(impostorObject, true);
         };
         };
@@ -102136,10 +102150,12 @@ var BABYLON;
         /**
         /**
          * Instantiates a UtilityLayerRenderer
          * Instantiates a UtilityLayerRenderer
          * @param originalScene the original scene that will be rendered on top of
          * @param originalScene the original scene that will be rendered on top of
+         * @param handleEvents boolean indicating if the utility layer should handle events
          */
          */
         function UtilityLayerRenderer(
         function UtilityLayerRenderer(
         /** the original scene that will be rendered on top of */
         /** the original scene that will be rendered on top of */
-        originalScene) {
+        originalScene, handleEvents) {
+            if (handleEvents === void 0) { handleEvents = true; }
             var _this = this;
             var _this = this;
             this.originalScene = originalScene;
             this.originalScene = originalScene;
             this._pointerCaptures = {};
             this._pointerCaptures = {};
@@ -102171,92 +102187,94 @@ var BABYLON;
             originalScene.getEngine().scenes.pop();
             originalScene.getEngine().scenes.pop();
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
             this.utilityLayerScene.detachControl();
-            this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
-                if (!_this.processAllEvents) {
-                    if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
-                        return;
-                    }
-                }
-                var pointerEvent = (prePointerInfo.event);
-                if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
-                    _this._pointerCaptures[pointerEvent.pointerId] = false;
-                    return;
-                }
-                var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
-                if (!prePointerInfo.ray && utilityScenePick) {
-                    prePointerInfo.ray = utilityScenePick.ray;
-                }
-                // always fire the prepointer oversvable
-                _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
-                // allow every non pointer down event to flow to the utility layer
-                if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!prePointerInfo.skipOnPointerObservable) {
-                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+            if (handleEvents) {
+                this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
+                    if (!_this.processAllEvents) {
+                        if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
+                            return;
+                        }
                     }
                     }
-                    if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                    var pointerEvent = (prePointerInfo.event);
+                    if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        return;
                     }
                     }
-                    return;
-                }
-                if (_this.utilityLayerScene.autoClearDepthAndStencil || _this.pickUtilitySceneFirst) {
-                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
-                    if (utilityScenePick && utilityScenePick.hit) {
+                    var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    if (!prePointerInfo.ray && utilityScenePick) {
+                        prePointerInfo.ray = utilityScenePick.ray;
+                    }
+                    // always fire the prepointer oversvable
+                    _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                    // allow every non pointer down event to flow to the utility layer
+                    if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                             _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                         }
                         }
-                        prePointerInfo.skipOnPointerObservable = true;
-                    }
-                }
-                else {
-                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
-                    var pointerEvent_1 = (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 && originalScenePick.pickedMesh) {
-                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
-                                // We touched an utility mesh present in the main scene
-                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent_1);
-                                prePointerInfo.skipOnPointerObservable = true;
-                            }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                                _this._pointerCaptures[pointerEvent_1.pointerId] = true;
-                            }
-                            else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
-                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
-                                delete _this._lastPointerEvents[pointerEvent_1.pointerId];
-                            }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
                         }
                         }
-                        else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            // We pick something in utility scene or the pick in utility is closer than the one in main scene
-                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent_1);
-                            // If a previous utility layer set this, do not unset this
+                        return;
+                    }
+                    if (_this.utilityLayerScene.autoClearDepthAndStencil || _this.pickUtilitySceneFirst) {
+                        // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                        if (utilityScenePick && utilityScenePick.hit) {
                             if (!prePointerInfo.skipOnPointerObservable) {
                             if (!prePointerInfo.skipOnPointerObservable) {
-                                prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             }
                             }
+                            prePointerInfo.skipOnPointerObservable = true;
                         }
                         }
-                        else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // 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_1);
-                                prePointerInfo.skipOnPointerObservable = true;
+                    }
+                    else {
+                        var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                        var pointerEvent_1 = (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 && originalScenePick.pickedMesh) {
+                                if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                    // We touched an utility mesh present in the main scene
+                                    _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent_1);
+                                    prePointerInfo.skipOnPointerObservable = true;
+                                }
+                                else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                                    _this._pointerCaptures[pointerEvent_1.pointerId] = true;
+                                }
+                                else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
+                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                    _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
+                                    delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                                }
                             }
                             }
-                            else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
-                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
-                                delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                            else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
+                                // We pick something in utility scene or the pick in utility is closer than the one in main scene
+                                _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent_1);
+                                // If a previous utility layer set this, do not unset this
+                                if (!prePointerInfo.skipOnPointerObservable) {
+                                    prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                }
+                            }
+                            else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
+                                // 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_1);
+                                    prePointerInfo.skipOnPointerObservable = true;
+                                }
+                                else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
+                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                    _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
+                                    delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                                }
+                            }
+                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent_1.pointerId]) {
+                                _this._pointerCaptures[pointerEvent_1.pointerId] = false;
                             }
                             }
-                        }
-                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent_1.pointerId]) {
-                            _this._pointerCaptures[pointerEvent_1.pointerId] = false;
                         }
                         }
                     }
                     }
-                }
-            });
+                });
+            }
             // Render directly on top of existing scene without clearing
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this.utilityLayerScene.autoClear = false;
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {

+ 106 - 88
dist/preview release/babylon.no-module.max.js

@@ -66972,6 +66972,9 @@ var BABYLON;
                 if (physicEngine) {
                 if (physicEngine) {
                     this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                     this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                 }
                 }
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene, false);
+                this._utilityLayer.pickUtilitySceneFirst = false;
+                this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
             }
             }
             /** @hidden */
             /** @hidden */
             PhysicsViewer.prototype._updateDebugMeshes = function () {
             PhysicsViewer.prototype._updateDebugMeshes = function () {
@@ -67006,7 +67009,7 @@ var BABYLON;
                         return null;
                         return null;
                     }
                     }
                 }
                 }
-                var debugMesh = this._getDebugMesh(impostor, this._scene);
+                var debugMesh = this._getDebugMesh(impostor);
                 if (debugMesh) {
                 if (debugMesh) {
                     this._impostors[this._numMeshes] = impostor;
                     this._impostors[this._numMeshes] = impostor;
                     this._meshes[this._numMeshes] = debugMesh;
                     this._meshes[this._numMeshes] = debugMesh;
@@ -67023,17 +67026,18 @@ var BABYLON;
              * @param impostor defines the impostor to hide
              * @param impostor defines the impostor to hide
              */
              */
             PhysicsViewer.prototype.hideImpostor = function (impostor) {
             PhysicsViewer.prototype.hideImpostor = function (impostor) {
-                if (!impostor || !this._scene) {
+                if (!impostor || !this._scene || !this._utilityLayer) {
                     return;
                     return;
                 }
                 }
                 var removed = false;
                 var removed = false;
+                var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 for (var i = 0; i < this._numMeshes; i++) {
                 for (var i = 0; i < this._numMeshes; i++) {
                     if (this._impostors[i] == impostor) {
                     if (this._impostors[i] == impostor) {
                         var mesh = this._meshes[i];
                         var mesh = this._meshes[i];
                         if (!mesh) {
                         if (!mesh) {
                             continue;
                             continue;
                         }
                         }
-                        this._scene.removeMesh(mesh);
+                        utilityLayerScene.removeMesh(mesh);
                         mesh.dispose();
                         mesh.dispose();
                         this._numMeshes--;
                         this._numMeshes--;
                         if (this._numMeshes > 0) {
                         if (this._numMeshes > 0) {
@@ -67058,37 +67062,39 @@ var BABYLON;
                 if (!this._debugMaterial) {
                 if (!this._debugMaterial) {
                     this._debugMaterial = new BABYLON.StandardMaterial('', scene);
                     this._debugMaterial = new BABYLON.StandardMaterial('', scene);
                     this._debugMaterial.wireframe = true;
                     this._debugMaterial.wireframe = true;
+                    this._debugMaterial.emissiveColor = BABYLON.Color3.White();
+                    this._debugMaterial.disableLighting = true;
                 }
                 }
                 return this._debugMaterial;
                 return this._debugMaterial;
             };
             };
             PhysicsViewer.prototype._getDebugBoxMesh = function (scene) {
             PhysicsViewer.prototype._getDebugBoxMesh = function (scene) {
                 if (!this._debugBoxMesh) {
                 if (!this._debugBoxMesh) {
                     this._debugBoxMesh = BABYLON.MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
                     this._debugBoxMesh = BABYLON.MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
-                    this._debugBoxMesh.renderingGroupId = 1;
                     this._debugBoxMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugBoxMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugBoxMesh.material = this._getDebugMaterial(scene);
                     this._debugBoxMesh.material = this._getDebugMaterial(scene);
-                    scene.removeMesh(this._debugBoxMesh);
                 }
                 }
                 return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
                 return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
             };
             };
             PhysicsViewer.prototype._getDebugSphereMesh = function (scene) {
             PhysicsViewer.prototype._getDebugSphereMesh = function (scene) {
                 if (!this._debugSphereMesh) {
                 if (!this._debugSphereMesh) {
                     this._debugSphereMesh = BABYLON.MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
                     this._debugSphereMesh = BABYLON.MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
-                    this._debugSphereMesh.renderingGroupId = 1;
                     this._debugSphereMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugSphereMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugSphereMesh.material = this._getDebugMaterial(scene);
                     this._debugSphereMesh.material = this._getDebugMaterial(scene);
-                    scene.removeMesh(this._debugSphereMesh);
                 }
                 }
                 return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
                 return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
             };
             };
-            PhysicsViewer.prototype._getDebugMesh = function (impostor, scene) {
+            PhysicsViewer.prototype._getDebugMesh = function (impostor) {
+                if (!this._utilityLayer) {
+                    return null;
+                }
                 var mesh = null;
                 var mesh = null;
+                var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 if (impostor.type == BABYLON.PhysicsImpostor.BoxImpostor) {
                 if (impostor.type == BABYLON.PhysicsImpostor.BoxImpostor) {
-                    mesh = this._getDebugBoxMesh(scene);
+                    mesh = this._getDebugBoxMesh(utilityLayerScene);
                     impostor.getBoxSizeToRef(mesh.scaling);
                     impostor.getBoxSizeToRef(mesh.scaling);
                 }
                 }
                 else if (impostor.type == BABYLON.PhysicsImpostor.SphereImpostor) {
                 else if (impostor.type == BABYLON.PhysicsImpostor.SphereImpostor) {
-                    mesh = this._getDebugSphereMesh(scene);
+                    mesh = this._getDebugSphereMesh(utilityLayerScene);
                     var radius = impostor.getRadius();
                     var radius = impostor.getRadius();
                     mesh.scaling.x = radius * 2;
                     mesh.scaling.x = radius * 2;
                     mesh.scaling.y = radius * 2;
                     mesh.scaling.y = radius * 2;
@@ -67113,6 +67119,10 @@ var BABYLON;
                 this._impostors.length = 0;
                 this._impostors.length = 0;
                 this._scene = null;
                 this._scene = null;
                 this._physicsEnginePlugin = null;
                 this._physicsEnginePlugin = null;
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                    this._utilityLayer = null;
+                }
             };
             };
             return PhysicsViewer;
             return PhysicsViewer;
         }());
         }());
@@ -79850,9 +79860,11 @@ var BABYLON;
                 }
                 }
                 // Environment texture
                 // Environment texture
                 if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
                 if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
+                    // PBR needed for both HDR texture (gamma space) & a sky box
+                    var isPBR = parsedData.isPBR !== undefined ? parsedData.isPBR : true;
                     if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
                     if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
                         var hdrSize = (parsedData.environmentTextureSize) ? parsedData.environmentTextureSize : 128;
                         var hdrSize = (parsedData.environmentTextureSize) ? parsedData.environmentTextureSize : 128;
-                        var hdrTexture = new BABYLON.HDRCubeTexture(rootUrl + parsedData.environmentTexture, scene, hdrSize);
+                        var hdrTexture = new BABYLON.HDRCubeTexture(rootUrl + parsedData.environmentTexture, scene, hdrSize, true, !isPBR);
                         if (parsedData.environmentTextureRotationY) {
                         if (parsedData.environmentTextureRotationY) {
                             hdrTexture.rotationY = parsedData.environmentTextureRotationY;
                             hdrTexture.rotationY = parsedData.environmentTextureRotationY;
                         }
                         }
@@ -79868,8 +79880,7 @@ var BABYLON;
                     if (parsedData.createDefaultSkybox === true) {
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
-                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
+                        scene.createDefaultSkybox(scene.environmentTexture, isPBR, skyboxScale, skyboxBlurLevel);
                     }
                     }
                 }
                 }
                 // Finish
                 // Finish
@@ -98893,6 +98904,7 @@ var BABYLON;
                 this._originDirection = this._origin.subtract(this._originTop).normalize();
                 this._originDirection = this._origin.subtract(this._originTop).normalize();
             }
             }
             this._tickCallback = this._tick.bind(this);
             this._tickCallback = this._tick.bind(this);
+            this._prepareCylinder();
         }
         }
         /**
         /**
          * Returns the data related to the updraft event (cylinder).
          * Returns the data related to the updraft event (cylinder).
@@ -98924,6 +98936,9 @@ var BABYLON;
         PhysicsUpdraftEvent.prototype.dispose = function (force) {
         PhysicsUpdraftEvent.prototype.dispose = function (force) {
             var _this = this;
             var _this = this;
             if (force === void 0) { force = true; }
             if (force === void 0) { force = true; }
+            if (!this._cylinder) {
+                return;
+            }
             if (force) {
             if (force) {
                 this._cylinder.dispose();
                 this._cylinder.dispose();
             }
             }
@@ -98975,7 +98990,6 @@ var BABYLON;
         };
         };
         PhysicsUpdraftEvent.prototype._intersectsWithCylinder = function (impostor) {
         PhysicsUpdraftEvent.prototype._intersectsWithCylinder = function (impostor) {
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
-            this._prepareCylinder();
             this._cylinder.position = this._cylinderPosition;
             this._cylinder.position = this._cylinderPosition;
             return this._cylinder.intersectsMesh(impostorObject, true);
             return this._cylinder.intersectsMesh(impostorObject, true);
         };
         };
@@ -99010,6 +99024,7 @@ var BABYLON;
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._tickCallback = this._tick.bind(this);
             this._tickCallback = this._tick.bind(this);
+            this._prepareCylinder();
         }
         }
         /**
         /**
          * Returns the data related to the vortex event (cylinder).
          * Returns the data related to the vortex event (cylinder).
@@ -99114,7 +99129,6 @@ var BABYLON;
         };
         };
         PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
         PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
-            this._prepareCylinder();
             this._cylinder.position = this._cylinderPosition;
             this._cylinder.position = this._cylinderPosition;
             return this._cylinder.intersectsMesh(impostorObject, true);
             return this._cylinder.intersectsMesh(impostorObject, true);
         };
         };
@@ -102103,10 +102117,12 @@ var BABYLON;
         /**
         /**
          * Instantiates a UtilityLayerRenderer
          * Instantiates a UtilityLayerRenderer
          * @param originalScene the original scene that will be rendered on top of
          * @param originalScene the original scene that will be rendered on top of
+         * @param handleEvents boolean indicating if the utility layer should handle events
          */
          */
         function UtilityLayerRenderer(
         function UtilityLayerRenderer(
         /** the original scene that will be rendered on top of */
         /** the original scene that will be rendered on top of */
-        originalScene) {
+        originalScene, handleEvents) {
+            if (handleEvents === void 0) { handleEvents = true; }
             var _this = this;
             var _this = this;
             this.originalScene = originalScene;
             this.originalScene = originalScene;
             this._pointerCaptures = {};
             this._pointerCaptures = {};
@@ -102138,92 +102154,94 @@ var BABYLON;
             originalScene.getEngine().scenes.pop();
             originalScene.getEngine().scenes.pop();
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
             this.utilityLayerScene.detachControl();
-            this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
-                if (!_this.processAllEvents) {
-                    if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
-                        return;
-                    }
-                }
-                var pointerEvent = (prePointerInfo.event);
-                if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
-                    _this._pointerCaptures[pointerEvent.pointerId] = false;
-                    return;
-                }
-                var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
-                if (!prePointerInfo.ray && utilityScenePick) {
-                    prePointerInfo.ray = utilityScenePick.ray;
-                }
-                // always fire the prepointer oversvable
-                _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
-                // allow every non pointer down event to flow to the utility layer
-                if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!prePointerInfo.skipOnPointerObservable) {
-                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+            if (handleEvents) {
+                this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
+                    if (!_this.processAllEvents) {
+                        if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
+                            return;
+                        }
                     }
                     }
-                    if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                    var pointerEvent = (prePointerInfo.event);
+                    if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        return;
                     }
                     }
-                    return;
-                }
-                if (_this.utilityLayerScene.autoClearDepthAndStencil || _this.pickUtilitySceneFirst) {
-                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
-                    if (utilityScenePick && utilityScenePick.hit) {
+                    var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    if (!prePointerInfo.ray && utilityScenePick) {
+                        prePointerInfo.ray = utilityScenePick.ray;
+                    }
+                    // always fire the prepointer oversvable
+                    _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                    // allow every non pointer down event to flow to the utility layer
+                    if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                             _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                         }
                         }
-                        prePointerInfo.skipOnPointerObservable = true;
-                    }
-                }
-                else {
-                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
-                    var pointerEvent_1 = (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 && originalScenePick.pickedMesh) {
-                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
-                                // We touched an utility mesh present in the main scene
-                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent_1);
-                                prePointerInfo.skipOnPointerObservable = true;
-                            }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                                _this._pointerCaptures[pointerEvent_1.pointerId] = true;
-                            }
-                            else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
-                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
-                                delete _this._lastPointerEvents[pointerEvent_1.pointerId];
-                            }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
                         }
                         }
-                        else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            // We pick something in utility scene or the pick in utility is closer than the one in main scene
-                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent_1);
-                            // If a previous utility layer set this, do not unset this
+                        return;
+                    }
+                    if (_this.utilityLayerScene.autoClearDepthAndStencil || _this.pickUtilitySceneFirst) {
+                        // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                        if (utilityScenePick && utilityScenePick.hit) {
                             if (!prePointerInfo.skipOnPointerObservable) {
                             if (!prePointerInfo.skipOnPointerObservable) {
-                                prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             }
                             }
+                            prePointerInfo.skipOnPointerObservable = true;
                         }
                         }
-                        else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // 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_1);
-                                prePointerInfo.skipOnPointerObservable = true;
+                    }
+                    else {
+                        var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                        var pointerEvent_1 = (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 && originalScenePick.pickedMesh) {
+                                if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                    // We touched an utility mesh present in the main scene
+                                    _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent_1);
+                                    prePointerInfo.skipOnPointerObservable = true;
+                                }
+                                else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                                    _this._pointerCaptures[pointerEvent_1.pointerId] = true;
+                                }
+                                else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
+                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                    _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
+                                    delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                                }
                             }
                             }
-                            else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
-                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
-                                delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                            else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
+                                // We pick something in utility scene or the pick in utility is closer than the one in main scene
+                                _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent_1);
+                                // If a previous utility layer set this, do not unset this
+                                if (!prePointerInfo.skipOnPointerObservable) {
+                                    prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                }
+                            }
+                            else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
+                                // 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_1);
+                                    prePointerInfo.skipOnPointerObservable = true;
+                                }
+                                else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
+                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                    _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
+                                    delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                                }
+                            }
+                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent_1.pointerId]) {
+                                _this._pointerCaptures[pointerEvent_1.pointerId] = false;
                             }
                             }
-                        }
-                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent_1.pointerId]) {
-                            _this._pointerCaptures[pointerEvent_1.pointerId] = false;
                         }
                         }
                     }
                     }
-                }
-            });
+                });
+            }
             // Render directly on top of existing scene without clearing
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this.utilityLayerScene.autoClear = false;
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/babylon.worker.js


+ 106 - 88
dist/preview release/es6.js

@@ -66972,6 +66972,9 @@ var BABYLON;
                 if (physicEngine) {
                 if (physicEngine) {
                     this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                     this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                 }
                 }
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene, false);
+                this._utilityLayer.pickUtilitySceneFirst = false;
+                this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
             }
             }
             /** @hidden */
             /** @hidden */
             PhysicsViewer.prototype._updateDebugMeshes = function () {
             PhysicsViewer.prototype._updateDebugMeshes = function () {
@@ -67006,7 +67009,7 @@ var BABYLON;
                         return null;
                         return null;
                     }
                     }
                 }
                 }
-                var debugMesh = this._getDebugMesh(impostor, this._scene);
+                var debugMesh = this._getDebugMesh(impostor);
                 if (debugMesh) {
                 if (debugMesh) {
                     this._impostors[this._numMeshes] = impostor;
                     this._impostors[this._numMeshes] = impostor;
                     this._meshes[this._numMeshes] = debugMesh;
                     this._meshes[this._numMeshes] = debugMesh;
@@ -67023,17 +67026,18 @@ var BABYLON;
              * @param impostor defines the impostor to hide
              * @param impostor defines the impostor to hide
              */
              */
             PhysicsViewer.prototype.hideImpostor = function (impostor) {
             PhysicsViewer.prototype.hideImpostor = function (impostor) {
-                if (!impostor || !this._scene) {
+                if (!impostor || !this._scene || !this._utilityLayer) {
                     return;
                     return;
                 }
                 }
                 var removed = false;
                 var removed = false;
+                var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 for (var i = 0; i < this._numMeshes; i++) {
                 for (var i = 0; i < this._numMeshes; i++) {
                     if (this._impostors[i] == impostor) {
                     if (this._impostors[i] == impostor) {
                         var mesh = this._meshes[i];
                         var mesh = this._meshes[i];
                         if (!mesh) {
                         if (!mesh) {
                             continue;
                             continue;
                         }
                         }
-                        this._scene.removeMesh(mesh);
+                        utilityLayerScene.removeMesh(mesh);
                         mesh.dispose();
                         mesh.dispose();
                         this._numMeshes--;
                         this._numMeshes--;
                         if (this._numMeshes > 0) {
                         if (this._numMeshes > 0) {
@@ -67058,37 +67062,39 @@ var BABYLON;
                 if (!this._debugMaterial) {
                 if (!this._debugMaterial) {
                     this._debugMaterial = new BABYLON.StandardMaterial('', scene);
                     this._debugMaterial = new BABYLON.StandardMaterial('', scene);
                     this._debugMaterial.wireframe = true;
                     this._debugMaterial.wireframe = true;
+                    this._debugMaterial.emissiveColor = BABYLON.Color3.White();
+                    this._debugMaterial.disableLighting = true;
                 }
                 }
                 return this._debugMaterial;
                 return this._debugMaterial;
             };
             };
             PhysicsViewer.prototype._getDebugBoxMesh = function (scene) {
             PhysicsViewer.prototype._getDebugBoxMesh = function (scene) {
                 if (!this._debugBoxMesh) {
                 if (!this._debugBoxMesh) {
                     this._debugBoxMesh = BABYLON.MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
                     this._debugBoxMesh = BABYLON.MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
-                    this._debugBoxMesh.renderingGroupId = 1;
                     this._debugBoxMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugBoxMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugBoxMesh.material = this._getDebugMaterial(scene);
                     this._debugBoxMesh.material = this._getDebugMaterial(scene);
-                    scene.removeMesh(this._debugBoxMesh);
                 }
                 }
                 return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
                 return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
             };
             };
             PhysicsViewer.prototype._getDebugSphereMesh = function (scene) {
             PhysicsViewer.prototype._getDebugSphereMesh = function (scene) {
                 if (!this._debugSphereMesh) {
                 if (!this._debugSphereMesh) {
                     this._debugSphereMesh = BABYLON.MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
                     this._debugSphereMesh = BABYLON.MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
-                    this._debugSphereMesh.renderingGroupId = 1;
                     this._debugSphereMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugSphereMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     this._debugSphereMesh.material = this._getDebugMaterial(scene);
                     this._debugSphereMesh.material = this._getDebugMaterial(scene);
-                    scene.removeMesh(this._debugSphereMesh);
                 }
                 }
                 return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
                 return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
             };
             };
-            PhysicsViewer.prototype._getDebugMesh = function (impostor, scene) {
+            PhysicsViewer.prototype._getDebugMesh = function (impostor) {
+                if (!this._utilityLayer) {
+                    return null;
+                }
                 var mesh = null;
                 var mesh = null;
+                var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 if (impostor.type == BABYLON.PhysicsImpostor.BoxImpostor) {
                 if (impostor.type == BABYLON.PhysicsImpostor.BoxImpostor) {
-                    mesh = this._getDebugBoxMesh(scene);
+                    mesh = this._getDebugBoxMesh(utilityLayerScene);
                     impostor.getBoxSizeToRef(mesh.scaling);
                     impostor.getBoxSizeToRef(mesh.scaling);
                 }
                 }
                 else if (impostor.type == BABYLON.PhysicsImpostor.SphereImpostor) {
                 else if (impostor.type == BABYLON.PhysicsImpostor.SphereImpostor) {
-                    mesh = this._getDebugSphereMesh(scene);
+                    mesh = this._getDebugSphereMesh(utilityLayerScene);
                     var radius = impostor.getRadius();
                     var radius = impostor.getRadius();
                     mesh.scaling.x = radius * 2;
                     mesh.scaling.x = radius * 2;
                     mesh.scaling.y = radius * 2;
                     mesh.scaling.y = radius * 2;
@@ -67113,6 +67119,10 @@ var BABYLON;
                 this._impostors.length = 0;
                 this._impostors.length = 0;
                 this._scene = null;
                 this._scene = null;
                 this._physicsEnginePlugin = null;
                 this._physicsEnginePlugin = null;
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                    this._utilityLayer = null;
+                }
             };
             };
             return PhysicsViewer;
             return PhysicsViewer;
         }());
         }());
@@ -79850,9 +79860,11 @@ var BABYLON;
                 }
                 }
                 // Environment texture
                 // Environment texture
                 if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
                 if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
+                    // PBR needed for both HDR texture (gamma space) & a sky box
+                    var isPBR = parsedData.isPBR !== undefined ? parsedData.isPBR : true;
                     if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
                     if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
                         var hdrSize = (parsedData.environmentTextureSize) ? parsedData.environmentTextureSize : 128;
                         var hdrSize = (parsedData.environmentTextureSize) ? parsedData.environmentTextureSize : 128;
-                        var hdrTexture = new BABYLON.HDRCubeTexture(rootUrl + parsedData.environmentTexture, scene, hdrSize);
+                        var hdrTexture = new BABYLON.HDRCubeTexture(rootUrl + parsedData.environmentTexture, scene, hdrSize, true, !isPBR);
                         if (parsedData.environmentTextureRotationY) {
                         if (parsedData.environmentTextureRotationY) {
                             hdrTexture.rotationY = parsedData.environmentTextureRotationY;
                             hdrTexture.rotationY = parsedData.environmentTextureRotationY;
                         }
                         }
@@ -79868,8 +79880,7 @@ var BABYLON;
                     if (parsedData.createDefaultSkybox === true) {
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
-                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
+                        scene.createDefaultSkybox(scene.environmentTexture, isPBR, skyboxScale, skyboxBlurLevel);
                     }
                     }
                 }
                 }
                 // Finish
                 // Finish
@@ -98893,6 +98904,7 @@ var BABYLON;
                 this._originDirection = this._origin.subtract(this._originTop).normalize();
                 this._originDirection = this._origin.subtract(this._originTop).normalize();
             }
             }
             this._tickCallback = this._tick.bind(this);
             this._tickCallback = this._tick.bind(this);
+            this._prepareCylinder();
         }
         }
         /**
         /**
          * Returns the data related to the updraft event (cylinder).
          * Returns the data related to the updraft event (cylinder).
@@ -98924,6 +98936,9 @@ var BABYLON;
         PhysicsUpdraftEvent.prototype.dispose = function (force) {
         PhysicsUpdraftEvent.prototype.dispose = function (force) {
             var _this = this;
             var _this = this;
             if (force === void 0) { force = true; }
             if (force === void 0) { force = true; }
+            if (!this._cylinder) {
+                return;
+            }
             if (force) {
             if (force) {
                 this._cylinder.dispose();
                 this._cylinder.dispose();
             }
             }
@@ -98975,7 +98990,6 @@ var BABYLON;
         };
         };
         PhysicsUpdraftEvent.prototype._intersectsWithCylinder = function (impostor) {
         PhysicsUpdraftEvent.prototype._intersectsWithCylinder = function (impostor) {
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
-            this._prepareCylinder();
             this._cylinder.position = this._cylinderPosition;
             this._cylinder.position = this._cylinderPosition;
             return this._cylinder.intersectsMesh(impostorObject, true);
             return this._cylinder.intersectsMesh(impostorObject, true);
         };
         };
@@ -99010,6 +99024,7 @@ var BABYLON;
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._tickCallback = this._tick.bind(this);
             this._tickCallback = this._tick.bind(this);
+            this._prepareCylinder();
         }
         }
         /**
         /**
          * Returns the data related to the vortex event (cylinder).
          * Returns the data related to the vortex event (cylinder).
@@ -99114,7 +99129,6 @@ var BABYLON;
         };
         };
         PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
         PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
-            this._prepareCylinder();
             this._cylinder.position = this._cylinderPosition;
             this._cylinder.position = this._cylinderPosition;
             return this._cylinder.intersectsMesh(impostorObject, true);
             return this._cylinder.intersectsMesh(impostorObject, true);
         };
         };
@@ -102103,10 +102117,12 @@ var BABYLON;
         /**
         /**
          * Instantiates a UtilityLayerRenderer
          * Instantiates a UtilityLayerRenderer
          * @param originalScene the original scene that will be rendered on top of
          * @param originalScene the original scene that will be rendered on top of
+         * @param handleEvents boolean indicating if the utility layer should handle events
          */
          */
         function UtilityLayerRenderer(
         function UtilityLayerRenderer(
         /** the original scene that will be rendered on top of */
         /** the original scene that will be rendered on top of */
-        originalScene) {
+        originalScene, handleEvents) {
+            if (handleEvents === void 0) { handleEvents = true; }
             var _this = this;
             var _this = this;
             this.originalScene = originalScene;
             this.originalScene = originalScene;
             this._pointerCaptures = {};
             this._pointerCaptures = {};
@@ -102138,92 +102154,94 @@ var BABYLON;
             originalScene.getEngine().scenes.pop();
             originalScene.getEngine().scenes.pop();
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
             this.utilityLayerScene.detachControl();
-            this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
-                if (!_this.processAllEvents) {
-                    if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
-                        return;
-                    }
-                }
-                var pointerEvent = (prePointerInfo.event);
-                if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
-                    _this._pointerCaptures[pointerEvent.pointerId] = false;
-                    return;
-                }
-                var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
-                if (!prePointerInfo.ray && utilityScenePick) {
-                    prePointerInfo.ray = utilityScenePick.ray;
-                }
-                // always fire the prepointer oversvable
-                _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
-                // allow every non pointer down event to flow to the utility layer
-                if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!prePointerInfo.skipOnPointerObservable) {
-                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+            if (handleEvents) {
+                this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
+                    if (!_this.processAllEvents) {
+                        if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
+                            return;
+                        }
                     }
                     }
-                    if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                    var pointerEvent = (prePointerInfo.event);
+                    if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        return;
                     }
                     }
-                    return;
-                }
-                if (_this.utilityLayerScene.autoClearDepthAndStencil || _this.pickUtilitySceneFirst) {
-                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
-                    if (utilityScenePick && utilityScenePick.hit) {
+                    var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    if (!prePointerInfo.ray && utilityScenePick) {
+                        prePointerInfo.ray = utilityScenePick.ray;
+                    }
+                    // always fire the prepointer oversvable
+                    _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                    // allow every non pointer down event to flow to the utility layer
+                    if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                             _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                         }
                         }
-                        prePointerInfo.skipOnPointerObservable = true;
-                    }
-                }
-                else {
-                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
-                    var pointerEvent_1 = (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 && originalScenePick.pickedMesh) {
-                            if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
-                                // We touched an utility mesh present in the main scene
-                                _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent_1);
-                                prePointerInfo.skipOnPointerObservable = true;
-                            }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                                _this._pointerCaptures[pointerEvent_1.pointerId] = true;
-                            }
-                            else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
-                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
-                                delete _this._lastPointerEvents[pointerEvent_1.pointerId];
-                            }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
                         }
                         }
-                        else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            // We pick something in utility scene or the pick in utility is closer than the one in main scene
-                            _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent_1);
-                            // If a previous utility layer set this, do not unset this
+                        return;
+                    }
+                    if (_this.utilityLayerScene.autoClearDepthAndStencil || _this.pickUtilitySceneFirst) {
+                        // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                        if (utilityScenePick && utilityScenePick.hit) {
                             if (!prePointerInfo.skipOnPointerObservable) {
                             if (!prePointerInfo.skipOnPointerObservable) {
-                                prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             }
                             }
+                            prePointerInfo.skipOnPointerObservable = true;
                         }
                         }
-                        else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // 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_1);
-                                prePointerInfo.skipOnPointerObservable = true;
+                    }
+                    else {
+                        var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                        var pointerEvent_1 = (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 && originalScenePick.pickedMesh) {
+                                if (_this.mainSceneTrackerPredicate && _this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                    // We touched an utility mesh present in the main scene
+                                    _this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent_1);
+                                    prePointerInfo.skipOnPointerObservable = true;
+                                }
+                                else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                                    _this._pointerCaptures[pointerEvent_1.pointerId] = true;
+                                }
+                                else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
+                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                    _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
+                                    delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                                }
                             }
                             }
-                            else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
-                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
-                                delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                            else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
+                                // We pick something in utility scene or the pick in utility is closer than the one in main scene
+                                _this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent_1);
+                                // If a previous utility layer set this, do not unset this
+                                if (!prePointerInfo.skipOnPointerObservable) {
+                                    prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                }
+                            }
+                            else if (!_this._pointerCaptures[pointerEvent_1.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
+                                // 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_1);
+                                    prePointerInfo.skipOnPointerObservable = true;
+                                }
+                                else if (_this._lastPointerEvents[pointerEvent_1.pointerId]) {
+                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                    _this.onPointerOutObservable.notifyObservers(pointerEvent_1.pointerId);
+                                    delete _this._lastPointerEvents[pointerEvent_1.pointerId];
+                                }
+                            }
+                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent_1.pointerId]) {
+                                _this._pointerCaptures[pointerEvent_1.pointerId] = false;
                             }
                             }
-                        }
-                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent_1.pointerId]) {
-                            _this._pointerCaptures[pointerEvent_1.pointerId] = false;
                         }
                         }
                     }
                     }
-                }
-            });
+                });
+            }
             // Render directly on top of existing scene without clearing
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this.utilityLayerScene.autoClear = false;
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {

+ 200 - 59
dist/preview release/viewer/babylon.viewer.d.ts

@@ -2,10 +2,6 @@
 /// <reference path="./babylon.glTF2Interface.d.ts"/>
 /// <reference path="./babylon.glTF2Interface.d.ts"/>
 /// <reference path="./babylonjs.loaders.d.ts"/>
 /// <reference path="./babylonjs.loaders.d.ts"/>
 declare module "babylonjs-loaders"{ export=BABYLON;}
 declare module "babylonjs-loaders"{ export=BABYLON;}
-/// <reference path="./babylon.d.ts"/>
-/// <reference path="./babylon.glTF2Interface.d.ts"/>
-/// <reference path="./babylonjs.loaders.d.ts"/>
-declare module "babylonjs-loaders"{ export=BABYLON;}
 // Generated by dts-bundle v0.7.3
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 // Dependencies for this module:
 //   ../../../../../Tools/gulp/babylonjs
 //   ../../../../../Tools/gulp/babylonjs
@@ -74,57 +70,6 @@ declare module BabylonViewer {
     export let viewerGlobals: ViewerGlobals;
     export let viewerGlobals: ViewerGlobals;
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
-    /**
-        * The viewer manager is the container for all viewers currently registered on this page.
-        * It is possible to have more than one viewer on a single page.
-        */
-    export class ViewerManager {
-            /**
-                * A callback that will be triggered when a new viewer was added
-                */
-            onViewerAdded: (viewer: AbstractViewer) => void;
-            /**
-                * Will notify when a new viewer was added
-                */
-            onViewerAddedObservable: BABYLON.Observable<AbstractViewer>;
-            /**
-                * Will notify when a viewer was removed (disposed)
-                */
-            onViewerRemovedObservable: BABYLON.Observable<string>;
-            constructor();
-            /**
-                * Adding a new viewer to the viewer manager and start tracking it.
-                * @param viewer the viewer to add
-                */
-            addViewer(viewer: AbstractViewer): void;
-            /**
-                * remove a viewer from the viewer manager
-                * @param viewer the viewer to remove
-                */
-            removeViewer(viewer: AbstractViewer): void;
-            /**
-                * Get a viewer by its baseId (if the container element has an ID, it is the this is. if not, a random id was assigned)
-                * @param id the id of the HTMl element (or the viewer's, if none provided)
-                */
-            getViewerById(id: string): AbstractViewer;
-            /**
-                * Get a viewer using a container element
-                * @param element the HTML element to search viewers associated with
-                */
-            getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
-            /**
-                * Get a promise that will fullfil when this viewer was initialized.
-                * Since viewer initialization and template injection is asynchronous, using the promise will guaranty that
-                * you will get the viewer after everything was already configured.
-                * @param id the viewer id to find
-                */
-            getViewerPromiseById(id: string): Promise<AbstractViewer>;
-            /**
-                * dispose the manager and all of its associated viewers
-                */
-            dispose(): void;
-    }
-    export let viewerManager: ViewerManager;
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
     /**
     /**
@@ -172,11 +117,11 @@ declare module BabylonViewer {
                 * Mainly used for help and errors
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
                 */
-            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
+            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
             /**
             /**
                 * Hide the overlay screen.
                 * Hide the overlay screen.
                 */
                 */
-            hideOverlayScreen(): Promise<string> | Promise<Template>;
+            hideOverlayScreen(): Promise<Template> | Promise<string>;
             /**
             /**
                 * show the viewer (in case it was hidden)
                 * show the viewer (in case it was hidden)
                 *
                 *
@@ -193,11 +138,11 @@ declare module BabylonViewer {
                 * Show the loading screen.
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 * The loading screen can be configured using the configuration object
                 */
                 */
-            showLoadingScreen(): Promise<string> | Promise<Template>;
+            showLoadingScreen(): Promise<Template> | Promise<string>;
             /**
             /**
                 * Hide the loading screen
                 * Hide the loading screen
                 */
                 */
-            hideLoadingScreen(): Promise<string> | Promise<Template>;
+            hideLoadingScreen(): Promise<Template> | Promise<string>;
             dispose(): void;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
     }
@@ -1042,6 +987,169 @@ declare module BabylonViewer {
     }
     }
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
+    /**
+        * The object sent when an event is triggered
+        */
+    export interface EventCallback {
+            event: Event;
+            template: Template;
+            selector: string;
+            payload?: any;
+    }
+    /**
+        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
+        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
+        */
+    export class TemplateManager {
+            containerElement: Element;
+            /**
+                * Will be triggered when any template is initialized
+                */
+            onTemplateInit: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when any template is fully loaded
+                */
+            onTemplateLoaded: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when a template state changes
+                */
+            onTemplateStateChange: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when all templates finished loading
+                */
+            onAllLoaded: BABYLON.Observable<TemplateManager>;
+            /**
+                * Will be triggered when any event on any template is triggered.
+                */
+            onEventTriggered: BABYLON.Observable<EventCallback>;
+            /**
+                * This template manager's event manager. In charge of callback registrations to native event types
+                */
+            eventManager: EventManager;
+            constructor(containerElement: Element);
+            /**
+                * Initialize the template(s) for the viewer. Called bay the Viewer class
+                * @param templates the templates to be used to initialize the main template
+                */
+            initTemplate(templates: {
+                    [key: string]: ITemplateConfiguration;
+            }): Promise<void>;
+            /**
+                * Get the canvas in the template tree.
+                * There must be one and only one canvas inthe template.
+                */
+            getCanvas(): HTMLCanvasElement | null;
+            /**
+                * Get a specific template from the template tree
+                * @param name the name of the template to load
+                */
+            getTemplate(name: string): Template | undefined;
+            /**
+                * Dispose the template manager
+                */
+            dispose(): void;
+    }
+    /**
+        * This class represents a single template in the viewer's template tree.
+        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
+        * A template is injected using the template manager in the correct position.
+        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
+        *
+        * For further information please refer to the documentation page, https://doc.babylonjs.com
+        */
+    export class Template {
+            name: string;
+            /**
+                * Will be triggered when the template is loaded
+                */
+            onLoaded: BABYLON.Observable<Template>;
+            /**
+                * will be triggered when the template is appended to the tree
+                */
+            onAppended: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when the template's state changed (shown, hidden)
+                */
+            onStateChange: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when an event is triggered on ths template.
+                * The event is a native browser event (like mouse or pointer events)
+                */
+            onEventTriggered: BABYLON.Observable<EventCallback>;
+            onParamsUpdated: BABYLON.Observable<Template>;
+            onHTMLRendered: BABYLON.Observable<Template>;
+            /**
+                * is the template loaded?
+                */
+            isLoaded: boolean;
+            /**
+                * This is meant to be used to track the show and hide functions.
+                * This is NOT (!!) a flag to check if the element is actually visible to the user.
+                */
+            isShown: boolean;
+            /**
+                * Is this template a part of the HTML tree (the template manager injected it)
+                */
+            isInHtmlTree: boolean;
+            /**
+                * The HTML element containing this template
+                */
+            parent: HTMLElement;
+            /**
+                * A promise that is fulfilled when the template finished loading.
+                */
+            initPromise: Promise<Template>;
+            constructor(name: string, _configuration: ITemplateConfiguration);
+            /**
+                * Some templates have parameters (like background color for example).
+                * The parameters are provided to Handlebars which in turn generates the template.
+                * This function will update the template with the new parameters
+                *
+                * Note that when updating parameters the events will be registered again (after being cleared).
+                *
+                * @param params the new template parameters
+                */
+            updateParams(params: {
+                    [key: string]: string | number | boolean | object;
+            }, append?: boolean): void;
+            redraw(): void;
+            /**
+                * Get the template'S configuration
+                */
+            readonly configuration: ITemplateConfiguration;
+            /**
+                * A template can be a parent element for other templates or HTML elements.
+                * This function will deliver all child HTML elements of this template.
+                */
+            getChildElements(): Array<string>;
+            /**
+                * Appending the template to a parent HTML element.
+                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
+                * @param parent the parent to which the template is added
+                * @param forceRemove if the parent already exists, shoud the template be removed from it?
+                */
+            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
+            /**
+                * Show the template using the provided visibilityFunction, or natively using display: flex.
+                * The provided function returns a promise that should be fullfilled when the element is shown.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Hide the template using the provided visibilityFunction, or natively using display: none.
+                * The provided function returns a promise that should be fullfilled when the element is hidden.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Dispose this template
+                */
+            dispose(): void;
+    }
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
     export class ConfigurationContainer {
     export class ConfigurationContainer {
@@ -1440,6 +1548,39 @@ declare module BabylonViewer {
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
     /**
     /**
+        * The EventManager is in charge of registering user interctions with the viewer.
+        * It is used in the TemplateManager
+        */
+    export class EventManager {
+            constructor(_templateManager: TemplateManager);
+            /**
+                * Register a new callback to a specific template.
+                * The best example for the usage can be found in the DefaultViewer
+                *
+                * @param templateName the templateName to register the event to
+                * @param callback The callback to be executed
+                * @param eventType the type of event to register
+                * @param selector an optional selector. if not defined the parent object in the template will be selected
+                */
+            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * This will remove a registered event from the defined template.
+                * Each one of the variables apart from the template name are optional, but one must be provided.
+                *
+                * @param templateName the templateName
+                * @param callback the callback to remove (optional)
+                * @param eventType the event type to remove (optional)
+                * @param selector the selector from which to remove the event (optional)
+                */
+            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * Dispose the event manager
+                */
+            dispose(): void;
+    }
+}
+declare module BabylonViewer {
+    /**
         * The ViewerLabs class will hold functions that are not (!) backwards compatible.
         * The ViewerLabs class will hold functions that are not (!) backwards compatible.
         * The APIs in all labs-related classes and configuration  might change.
         * The APIs in all labs-related classes and configuration  might change.
         * Once stable, lab features will be moved to the publis API and configuration object.
         * Once stable, lab features will be moved to the publis API and configuration object.

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 206 - 63
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -3,11 +3,6 @@
 /// <reference path="./babylonjs.loaders.d.ts"/>
 /// <reference path="./babylonjs.loaders.d.ts"/>
 declare module "babylonjs-loaders"{ export=BABYLON;}
 declare module "babylonjs-loaders"{ export=BABYLON;}
 
 
-/// <reference path="./babylon.d.ts"/>
-/// <reference path="./babylon.glTF2Interface.d.ts"/>
-/// <reference path="./babylonjs.loaders.d.ts"/>
-declare module "babylonjs-loaders"{ export=BABYLON;}
-
 // Generated by dts-bundle v0.7.3
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 // Dependencies for this module:
 //   ../../../../../Tools/gulp/babylonjs
 //   ../../../../../Tools/gulp/babylonjs
@@ -99,59 +94,7 @@ declare module 'babylonjs-viewer/configuration/globals' {
 }
 }
 
 
 declare module 'babylonjs-viewer/viewer/viewerManager' {
 declare module 'babylonjs-viewer/viewer/viewerManager' {
-    import { Observable } from 'babylonjs';
-    import { AbstractViewer } from 'babylonjs-viewer/viewer/viewer';
-    /**
-        * The viewer manager is the container for all viewers currently registered on this page.
-        * It is possible to have more than one viewer on a single page.
-        */
-    export class ViewerManager {
-            /**
-                * A callback that will be triggered when a new viewer was added
-                */
-            onViewerAdded: (viewer: AbstractViewer) => void;
-            /**
-                * Will notify when a new viewer was added
-                */
-            onViewerAddedObservable: Observable<AbstractViewer>;
-            /**
-                * Will notify when a viewer was removed (disposed)
-                */
-            onViewerRemovedObservable: Observable<string>;
-            constructor();
-            /**
-                * Adding a new viewer to the viewer manager and start tracking it.
-                * @param viewer the viewer to add
-                */
-            addViewer(viewer: AbstractViewer): void;
-            /**
-                * remove a viewer from the viewer manager
-                * @param viewer the viewer to remove
-                */
-            removeViewer(viewer: AbstractViewer): void;
-            /**
-                * Get a viewer by its baseId (if the container element has an ID, it is the this is. if not, a random id was assigned)
-                * @param id the id of the HTMl element (or the viewer's, if none provided)
-                */
-            getViewerById(id: string): AbstractViewer;
-            /**
-                * Get a viewer using a container element
-                * @param element the HTML element to search viewers associated with
-                */
-            getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
-            /**
-                * Get a promise that will fullfil when this viewer was initialized.
-                * Since viewer initialization and template injection is asynchronous, using the promise will guaranty that
-                * you will get the viewer after everything was already configured.
-                * @param id the viewer id to find
-                */
-            getViewerPromiseById(id: string): Promise<AbstractViewer>;
-            /**
-                * dispose the manager and all of its associated viewers
-                */
-            dispose(): void;
-    }
-    export let viewerManager: ViewerManager;
+    
 }
 }
 
 
 declare module 'babylonjs-viewer/viewer/defaultViewer' {
 declare module 'babylonjs-viewer/viewer/defaultViewer' {
@@ -205,11 +148,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Mainly used for help and errors
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
                 */
-            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
+            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
             /**
             /**
                 * Hide the overlay screen.
                 * Hide the overlay screen.
                 */
                 */
-            hideOverlayScreen(): Promise<string> | Promise<Template>;
+            hideOverlayScreen(): Promise<Template> | Promise<string>;
             /**
             /**
                 * show the viewer (in case it was hidden)
                 * show the viewer (in case it was hidden)
                 *
                 *
@@ -226,11 +169,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Show the loading screen.
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 * The loading screen can be configured using the configuration object
                 */
                 */
-            showLoadingScreen(): Promise<string> | Promise<Template>;
+            showLoadingScreen(): Promise<Template> | Promise<string>;
             /**
             /**
                 * Hide the loading screen
                 * Hide the loading screen
                 */
                 */
-            hideLoadingScreen(): Promise<string> | Promise<Template>;
+            hideLoadingScreen(): Promise<Template> | Promise<string>;
             dispose(): void;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
     }
@@ -1120,7 +1063,172 @@ declare module 'babylonjs-viewer/configuration/configuration' {
 }
 }
 
 
 declare module 'babylonjs-viewer/templating/templateManager' {
 declare module 'babylonjs-viewer/templating/templateManager' {
-    
+    import { Observable } from 'babylonjs';
+    import { EventManager } from 'babylonjs-viewer/templating/eventManager';
+    import { ITemplateConfiguration } from 'babylonjs-viewer/configuration/interfaces';
+    /**
+        * The object sent when an event is triggered
+        */
+    export interface EventCallback {
+            event: Event;
+            template: Template;
+            selector: string;
+            payload?: any;
+    }
+    /**
+        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
+        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
+        */
+    export class TemplateManager {
+            containerElement: Element;
+            /**
+                * Will be triggered when any template is initialized
+                */
+            onTemplateInit: Observable<Template>;
+            /**
+                * Will be triggered when any template is fully loaded
+                */
+            onTemplateLoaded: Observable<Template>;
+            /**
+                * Will be triggered when a template state changes
+                */
+            onTemplateStateChange: Observable<Template>;
+            /**
+                * Will be triggered when all templates finished loading
+                */
+            onAllLoaded: Observable<TemplateManager>;
+            /**
+                * Will be triggered when any event on any template is triggered.
+                */
+            onEventTriggered: Observable<EventCallback>;
+            /**
+                * This template manager's event manager. In charge of callback registrations to native event types
+                */
+            eventManager: EventManager;
+            constructor(containerElement: Element);
+            /**
+                * Initialize the template(s) for the viewer. Called bay the Viewer class
+                * @param templates the templates to be used to initialize the main template
+                */
+            initTemplate(templates: {
+                    [key: string]: ITemplateConfiguration;
+            }): Promise<void>;
+            /**
+                * Get the canvas in the template tree.
+                * There must be one and only one canvas inthe template.
+                */
+            getCanvas(): HTMLCanvasElement | null;
+            /**
+                * Get a specific template from the template tree
+                * @param name the name of the template to load
+                */
+            getTemplate(name: string): Template | undefined;
+            /**
+                * Dispose the template manager
+                */
+            dispose(): void;
+    }
+    /**
+        * This class represents a single template in the viewer's template tree.
+        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
+        * A template is injected using the template manager in the correct position.
+        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
+        *
+        * For further information please refer to the documentation page, https://doc.babylonjs.com
+        */
+    export class Template {
+            name: string;
+            /**
+                * Will be triggered when the template is loaded
+                */
+            onLoaded: Observable<Template>;
+            /**
+                * will be triggered when the template is appended to the tree
+                */
+            onAppended: Observable<Template>;
+            /**
+                * Will be triggered when the template's state changed (shown, hidden)
+                */
+            onStateChange: Observable<Template>;
+            /**
+                * Will be triggered when an event is triggered on ths template.
+                * The event is a native browser event (like mouse or pointer events)
+                */
+            onEventTriggered: Observable<EventCallback>;
+            onParamsUpdated: Observable<Template>;
+            onHTMLRendered: Observable<Template>;
+            /**
+                * is the template loaded?
+                */
+            isLoaded: boolean;
+            /**
+                * This is meant to be used to track the show and hide functions.
+                * This is NOT (!!) a flag to check if the element is actually visible to the user.
+                */
+            isShown: boolean;
+            /**
+                * Is this template a part of the HTML tree (the template manager injected it)
+                */
+            isInHtmlTree: boolean;
+            /**
+                * The HTML element containing this template
+                */
+            parent: HTMLElement;
+            /**
+                * A promise that is fulfilled when the template finished loading.
+                */
+            initPromise: Promise<Template>;
+            constructor(name: string, _configuration: ITemplateConfiguration);
+            /**
+                * Some templates have parameters (like background color for example).
+                * The parameters are provided to Handlebars which in turn generates the template.
+                * This function will update the template with the new parameters
+                *
+                * Note that when updating parameters the events will be registered again (after being cleared).
+                *
+                * @param params the new template parameters
+                */
+            updateParams(params: {
+                    [key: string]: string | number | boolean | object;
+            }, append?: boolean): void;
+            redraw(): void;
+            /**
+                * Get the template'S configuration
+                */
+            readonly configuration: ITemplateConfiguration;
+            /**
+                * A template can be a parent element for other templates or HTML elements.
+                * This function will deliver all child HTML elements of this template.
+                */
+            getChildElements(): Array<string>;
+            /**
+                * Appending the template to a parent HTML element.
+                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
+                * @param parent the parent to which the template is added
+                * @param forceRemove if the parent already exists, shoud the template be removed from it?
+                */
+            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
+            /**
+                * Show the template using the provided visibilityFunction, or natively using display: flex.
+                * The provided function returns a promise that should be fullfilled when the element is shown.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Hide the template using the provided visibilityFunction, or natively using display: none.
+                * The provided function returns a promise that should be fullfilled when the element is hidden.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Dispose this template
+                */
+            dispose(): void;
+    }
 }
 }
 
 
 declare module 'babylonjs-viewer/configuration/configurationContainer' {
 declare module 'babylonjs-viewer/configuration/configurationContainer' {
@@ -1562,6 +1670,41 @@ declare module 'babylonjs-viewer/configuration/interfaces/environmentMapConfigur
     }
     }
 }
 }
 
 
+declare module 'babylonjs-viewer/templating/eventManager' {
+    import { EventCallback, TemplateManager } from "babylonjs-viewer/templating/templateManager";
+    /**
+        * The EventManager is in charge of registering user interctions with the viewer.
+        * It is used in the TemplateManager
+        */
+    export class EventManager {
+            constructor(_templateManager: TemplateManager);
+            /**
+                * Register a new callback to a specific template.
+                * The best example for the usage can be found in the DefaultViewer
+                *
+                * @param templateName the templateName to register the event to
+                * @param callback The callback to be executed
+                * @param eventType the type of event to register
+                * @param selector an optional selector. if not defined the parent object in the template will be selected
+                */
+            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * This will remove a registered event from the defined template.
+                * Each one of the variables apart from the template name are optional, but one must be provided.
+                *
+                * @param templateName the templateName
+                * @param callback the callback to remove (optional)
+                * @param eventType the event type to remove (optional)
+                * @param selector the selector from which to remove the event (optional)
+                */
+            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * Dispose the event manager
+                */
+            dispose(): void;
+    }
+}
+
 declare module 'babylonjs-viewer/labs/viewerLabs' {
 declare module 'babylonjs-viewer/labs/viewerLabs' {
     import { PBREnvironment } from "babylonjs-viewer/labs/environmentSerializer";
     import { PBREnvironment } from "babylonjs-viewer/labs/environmentSerializer";
     import { ShadowLight, Vector3, Scene } from 'babylonjs';
     import { ShadowLight, Vector3, Scene } from 'babylonjs';

+ 24 - 12
src/Debug/babylon.physicsViewer.ts

@@ -16,6 +16,7 @@ module BABYLON.Debug {
         /** @hidden */
         /** @hidden */
         protected _physicsEnginePlugin: Nullable<IPhysicsEnginePlugin>;
         protected _physicsEnginePlugin: Nullable<IPhysicsEnginePlugin>;
         private _renderFunction: () => void;
         private _renderFunction: () => void;
+        private _utilityLayer: Nullable<UtilityLayerRenderer>;
 
 
         private _debugBoxMesh: Mesh;
         private _debugBoxMesh: Mesh;
         private _debugSphereMesh: Mesh;
         private _debugSphereMesh: Mesh;
@@ -32,11 +33,14 @@ module BABYLON.Debug {
             if (physicEngine) {
             if (physicEngine) {
                 this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
                 this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
             }
             }
+
+            this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
+            this._utilityLayer.pickUtilitySceneFirst = false;
+            this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
         }
         }
 
 
         /** @hidden */
         /** @hidden */
         protected _updateDebugMeshes(): void {
         protected _updateDebugMeshes(): void {
-
             var plugin = this._physicsEnginePlugin;
             var plugin = this._physicsEnginePlugin;
 
 
             for (var i = 0; i < this._numMeshes; i++) {
             for (var i = 0; i < this._numMeshes; i++) {
@@ -56,7 +60,6 @@ module BABYLON.Debug {
                     }
                     }
                 }
                 }
             }
             }
-
         }
         }
 
 
         /**
         /**
@@ -76,7 +79,7 @@ module BABYLON.Debug {
                 }
                 }
             }
             }
 
 
-            var debugMesh = this._getDebugMesh(impostor, this._scene);
+            var debugMesh = this._getDebugMesh(impostor);
 
 
             if (debugMesh) {
             if (debugMesh) {
                 this._impostors[this._numMeshes] = impostor;
                 this._impostors[this._numMeshes] = impostor;
@@ -99,11 +102,12 @@ module BABYLON.Debug {
          */
          */
         public hideImpostor(impostor: Nullable<PhysicsImpostor>) {
         public hideImpostor(impostor: Nullable<PhysicsImpostor>) {
 
 
-            if (!impostor || !this._scene) {
+            if (!impostor || !this._scene || !this._utilityLayer) {
                 return;
                 return;
             }
             }
 
 
             var removed = false;
             var removed = false;
+            const utilityLayerScene = this._utilityLayer.utilityLayerScene;
 
 
             for (var i = 0; i < this._numMeshes; i++) {
             for (var i = 0; i < this._numMeshes; i++) {
                 if (this._impostors[i] == impostor) {
                 if (this._impostors[i] == impostor) {
@@ -113,7 +117,7 @@ module BABYLON.Debug {
                         continue;
                         continue;
                     }
                     }
 
 
-                    this._scene.removeMesh(mesh);
+                    utilityLayerScene.removeMesh(mesh);
                     mesh.dispose();
                     mesh.dispose();
                     this._numMeshes--;
                     this._numMeshes--;
                     if (this._numMeshes > 0) {
                     if (this._numMeshes > 0) {
@@ -140,6 +144,8 @@ module BABYLON.Debug {
             if (!this._debugMaterial) {
             if (!this._debugMaterial) {
                 this._debugMaterial = new StandardMaterial('', scene);
                 this._debugMaterial = new StandardMaterial('', scene);
                 this._debugMaterial.wireframe = true;
                 this._debugMaterial.wireframe = true;
+                this._debugMaterial.emissiveColor = Color3.White();
+                this._debugMaterial.disableLighting = true;
             }
             }
 
 
             return this._debugMaterial;
             return this._debugMaterial;
@@ -148,10 +154,8 @@ module BABYLON.Debug {
         private _getDebugBoxMesh(scene: Scene): AbstractMesh {
         private _getDebugBoxMesh(scene: Scene): AbstractMesh {
             if (!this._debugBoxMesh) {
             if (!this._debugBoxMesh) {
                 this._debugBoxMesh = MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
                 this._debugBoxMesh = MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
-                this._debugBoxMesh.renderingGroupId = 1;
                 this._debugBoxMesh.rotationQuaternion = Quaternion.Identity();
                 this._debugBoxMesh.rotationQuaternion = Quaternion.Identity();
                 this._debugBoxMesh.material = this._getDebugMaterial(scene);
                 this._debugBoxMesh.material = this._getDebugMaterial(scene);
-                scene.removeMesh(this._debugBoxMesh);
             }
             }
 
 
             return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
             return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
@@ -160,23 +164,26 @@ module BABYLON.Debug {
         private _getDebugSphereMesh(scene: Scene): AbstractMesh {
         private _getDebugSphereMesh(scene: Scene): AbstractMesh {
             if (!this._debugSphereMesh) {
             if (!this._debugSphereMesh) {
                 this._debugSphereMesh = MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
                 this._debugSphereMesh = MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
-                this._debugSphereMesh.renderingGroupId = 1;
                 this._debugSphereMesh.rotationQuaternion = Quaternion.Identity();
                 this._debugSphereMesh.rotationQuaternion = Quaternion.Identity();
                 this._debugSphereMesh.material = this._getDebugMaterial(scene);
                 this._debugSphereMesh.material = this._getDebugMaterial(scene);
-                scene.removeMesh(this._debugSphereMesh);
             }
             }
 
 
             return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
             return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
         }
         }
 
 
-        private _getDebugMesh(impostor: PhysicsImpostor, scene: Scene): Nullable<AbstractMesh> {
+        private _getDebugMesh(impostor: PhysicsImpostor): Nullable<AbstractMesh> {
+            if (!this._utilityLayer) {
+                return null;
+            }
+
             var mesh: Nullable<AbstractMesh> = null;
             var mesh: Nullable<AbstractMesh> = null;
+            const utilityLayerScene = this._utilityLayer.utilityLayerScene;
 
 
             if (impostor.type == PhysicsImpostor.BoxImpostor) {
             if (impostor.type == PhysicsImpostor.BoxImpostor) {
-                mesh = this._getDebugBoxMesh(scene);
+                mesh = this._getDebugBoxMesh(utilityLayerScene);
                 impostor.getBoxSizeToRef(mesh.scaling);
                 impostor.getBoxSizeToRef(mesh.scaling);
             } else if (impostor.type == PhysicsImpostor.SphereImpostor) {
             } else if (impostor.type == PhysicsImpostor.SphereImpostor) {
-                mesh = this._getDebugSphereMesh(scene);
+                mesh = this._getDebugSphereMesh(utilityLayerScene);
                 var radius = impostor.getRadius();
                 var radius = impostor.getRadius();
                 mesh.scaling.x = radius * 2;
                 mesh.scaling.x = radius * 2;
                 mesh.scaling.y = radius * 2;
                 mesh.scaling.y = radius * 2;
@@ -205,6 +212,11 @@ module BABYLON.Debug {
             this._impostors.length = 0;
             this._impostors.length = 0;
             this._scene = null;
             this._scene = null;
             this._physicsEnginePlugin = null;
             this._physicsEnginePlugin = null;
+
+            if (this._utilityLayer) {
+                this._utilityLayer.dispose();
+                this._utilityLayer = null;
+            }
         }
         }
     }
     }
 }
 }

+ 78 - 73
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -71,10 +71,12 @@ module BABYLON {
         /**
         /**
          * Instantiates a UtilityLayerRenderer
          * Instantiates a UtilityLayerRenderer
          * @param originalScene the original scene that will be rendered on top of
          * @param originalScene the original scene that will be rendered on top of
+         * @param handleEvents boolean indicating if the utility layer should handle events
          */
          */
         constructor(
         constructor(
             /** the original scene that will be rendered on top of */
             /** the original scene that will be rendered on top of */
-            public originalScene: Scene) {
+            public originalScene: Scene,
+            handleEvents: boolean = true) {
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             this.utilityLayerScene.useRightHandedSystem = originalScene.useRightHandedSystem;
             this.utilityLayerScene.useRightHandedSystem = originalScene.useRightHandedSystem;
@@ -83,98 +85,101 @@ module BABYLON {
 
 
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
             this.utilityLayerScene.detachControl();
-            this._originalPointerObserver = originalScene.onPrePointerObservable.add((prePointerInfo, eventState) => {
 
 
-                if (!this.processAllEvents) {
-                    if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
-                        && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
-                        return;
-                    }
-                }
-
-                let pointerEvent = <PointerEvent>(prePointerInfo.event);
-                if (originalScene!.isPointerCaptured(pointerEvent.pointerId)) {
-                    this._pointerCaptures[pointerEvent.pointerId] = false;
-                    return;
-                }
+            if (handleEvents) {
+                this._originalPointerObserver = originalScene.onPrePointerObservable.add((prePointerInfo, eventState) => {
 
 
-                var utilityScenePick = prePointerInfo.ray ? this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
-                if (!prePointerInfo.ray && utilityScenePick) {
-                    prePointerInfo.ray = utilityScenePick.ray;
-                }
-
-                // always fire the prepointer oversvable
-                this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
-
-                // allow every non pointer down event to flow to the utility layer
-                if (this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!prePointerInfo.skipOnPointerObservable) {
-                        this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                    if (!this.processAllEvents) {
+                        if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
+                            && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
+                            return;
+                        }
                     }
                     }
-                    if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+
+                    let pointerEvent = <PointerEvent>(prePointerInfo.event);
+                    if (originalScene!.isPointerCaptured(pointerEvent.pointerId)) {
                         this._pointerCaptures[pointerEvent.pointerId] = false;
                         this._pointerCaptures[pointerEvent.pointerId] = false;
+                        return;
                     }
                     }
-                    return;
-                }
 
 
-                if (this.utilityLayerScene.autoClearDepthAndStencil || this.pickUtilitySceneFirst) {
-                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
-                    if (utilityScenePick && utilityScenePick.hit) {
+                    var utilityScenePick = prePointerInfo.ray ? this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    if (!prePointerInfo.ray && utilityScenePick) {
+                        prePointerInfo.ray = utilityScenePick.ray;
+                    }
+
+                    // always fire the prepointer oversvable
+                    this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
 
 
+                    // allow every non pointer down event to flow to the utility layer
+                    if (this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                             this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                         }
                         }
-                        prePointerInfo.skipOnPointerObservable = true;
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+                            this._pointerCaptures[pointerEvent.pointerId] = false;
+                        }
+                        return;
                     }
                     }
-                } else {
-                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
-                    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 (this.utilityLayerScene.autoClearDepthAndStencil || this.pickUtilitySceneFirst) {
+                        // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                        if (utilityScenePick && utilityScenePick.hit) {
 
 
-                        // No pick in utility scene
-                        if (utilityScenePick.distance === 0 && originalScenePick.pickedMesh) {
-                            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;
-                            } 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];
-                            }
-                        } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                            // We pick something in utility scene or the pick in utility is closer than the one in main scene
-                            this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
-                            // If a previous utility layer set this, do not unset this
                             if (!prePointerInfo.skipOnPointerObservable) {
                             if (!prePointerInfo.skipOnPointerObservable) {
-                                prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
-                            }
-                        } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                            // 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];
+                                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                             }
                             }
+                            prePointerInfo.skipOnPointerObservable = true;
                         }
                         }
+                    } else {
+                        var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                        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) {
+
+                            // No pick in utility scene
+                            if (utilityScenePick.distance === 0 && originalScenePick.pickedMesh) {
+                                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;
+                                } 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];
+                                }
+                            } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
+                                // We pick something in utility scene or the pick in utility is closer than the one in main scene
+                                this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
+                                // If a previous utility layer set this, do not unset this
+                                if (!prePointerInfo.skipOnPointerObservable) {
+                                    prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                                }
+                            } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
+                                // We have a pick in both scenes but main is closer than utility
 
 
-                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
-                            this._pointerCaptures[pointerEvent.pointerId] = false;
+                                // 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];
+                                }
+                            }
+
+                            if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+                                this._pointerCaptures[pointerEvent.pointerId] = false;
+                            }
                         }
                         }
                     }
                     }
-                }
 
 
-            });
+                });
+            }
 
 
             // Render directly on top of existing scene without clearing
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this.utilityLayerScene.autoClear = false;