David Catuhe 7 jaren geleden
bovenliggende
commit
0cca887f43

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


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


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


+ 201 - 2
dist/preview release/babylon.max.js

@@ -9470,6 +9470,10 @@ var BABYLON;
          * @param fileName defines the name of the downloaded file
          * @param fileName defines the name of the downloaded file
          */
          */
         Tools.Download = function (blob, fileName) {
         Tools.Download = function (blob, fileName) {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
             var url = window.URL.createObjectURL(blob);
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             var a = document.createElement("a");
             document.body.appendChild(a);
             document.body.appendChild(a);
@@ -24175,6 +24179,8 @@ var BABYLON;
              */
              */
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._activeMeshesFrozen = false;
             _this._activeMeshesFrozen = false;
+            /** @hidden */
+            _this._allowPostProcessClear = true;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine.scenes.push(_this);
             _this._engine.scenes.push(_this);
@@ -67162,7 +67168,7 @@ var BABYLON;
             }
             }
             this.onActivateObservable.notifyObservers(camera);
             this.onActivateObservable.notifyObservers(camera);
             // Clear
             // Clear
-            if (this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
+            if (scene._allowPostProcessClear && this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
             }
             }
             if (this._reusable) {
             if (this._reusable) {
@@ -89282,6 +89288,7 @@ var BABYLON;
             this.onPointerOutObservable = new BABYLON.Observable();
             this.onPointerOutObservable = new BABYLON.Observable();
             // 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._allowPostProcessClear = false;
             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();
@@ -89399,7 +89406,27 @@ var BABYLON;
          */
          */
         UtilityLayerRenderer.prototype.render = function () {
         UtilityLayerRenderer.prototype.render = function () {
             this._updateCamera();
             this._updateCamera();
-            this.utilityLayerScene.render(false);
+            if (this.utilityLayerScene.activeCamera) {
+                // Set the camera's scene to utility layers scene
+                var oldScene = this.utilityLayerScene.activeCamera.getScene();
+                var camera = this.utilityLayerScene.activeCamera;
+                camera._scene = this.utilityLayerScene;
+                if (camera.leftCamera) {
+                    camera.leftCamera._scene = this.utilityLayerScene;
+                }
+                if (camera.rightCamera) {
+                    camera.rightCamera._scene = this.utilityLayerScene;
+                }
+                this.utilityLayerScene.render(false);
+                // Reset camera's scene back to original
+                camera._scene = oldScene;
+                if (camera.leftCamera) {
+                    camera.leftCamera._scene = oldScene;
+                }
+                if (camera.rightCamera) {
+                    camera.rightCamera._scene = oldScene;
+                }
+            }
         };
         };
         /**
         /**
          * Disposes of the renderer
          * Disposes of the renderer
@@ -90005,6 +90032,178 @@ var BABYLON;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     /**
     /**
+     * @hidden
+     */
+    var FaceDirectionInfo = /** @class */ (function () {
+        function FaceDirectionInfo(direction, rotatedDirection, diff, ignore) {
+            if (rotatedDirection === void 0) { rotatedDirection = new BABYLON.Vector3(); }
+            if (diff === void 0) { diff = 0; }
+            if (ignore === void 0) { ignore = false; }
+            this.direction = direction;
+            this.rotatedDirection = rotatedDirection;
+            this.diff = diff;
+            this.ignore = ignore;
+        }
+        return FaceDirectionInfo;
+    }());
+    /**
+     * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+     */
+    var AttachToBoxBehavior = /** @class */ (function () {
+        /**
+         * Creates the AttachToBoxBehavior, used to attach UI to the closest face of the box to a camera
+         * @param ui The transform node that should be attched to the mesh
+         */
+        function AttachToBoxBehavior(ui) {
+            this.ui = ui;
+            /**
+             *  The name of the behavior
+             */
+            this.name = "AttachToBoxBehavior";
+            /**
+             * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
+             */
+            this.distanceAwayFromFace = 0.15;
+            /**
+             * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
+             */
+            this.distanceAwayFromBottomOfFace = 0.15;
+            this._faceVectors = [new FaceDirectionInfo(BABYLON.Vector3.Up()), new FaceDirectionInfo(BABYLON.Vector3.Down()), new FaceDirectionInfo(BABYLON.Vector3.Left()), new FaceDirectionInfo(BABYLON.Vector3.Right()), new FaceDirectionInfo(BABYLON.Vector3.Forward()), new FaceDirectionInfo(BABYLON.Vector3.Forward().scaleInPlace(-1))];
+            this._tmpMatrix = new BABYLON.Matrix();
+            this._tmpVector = new BABYLON.Vector3();
+            this._zeroVector = BABYLON.Vector3.Zero();
+            this._lookAtTmpMatrix = new BABYLON.Matrix();
+            /* Does nothing */
+        }
+        /**
+         *  Initializes the behavior
+         */
+        AttachToBoxBehavior.prototype.init = function () {
+            /* Does nothing */
+        };
+        AttachToBoxBehavior.prototype._closestFace = function (targetDirection) {
+            var _this = this;
+            // Go over each face and calculate the angle between the face's normal and targetDirection
+            this._faceVectors.forEach(function (v) {
+                if (!_this._target.rotationQuaternion) {
+                    _this._target.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(_this._target.rotation.y, _this._target.rotation.x, _this._target.rotation.z);
+                }
+                _this._target.rotationQuaternion.toRotationMatrix(_this._tmpMatrix);
+                BABYLON.Vector3.TransformCoordinatesToRef(v.direction, _this._tmpMatrix, v.rotatedDirection);
+                v.diff = BABYLON.Vector3.GetAngleBetweenVectors(v.rotatedDirection, targetDirection, BABYLON.Vector3.Cross(v.rotatedDirection, targetDirection));
+            });
+            // Return the face information of the one with the normal closeset to target direction
+            return this._faceVectors.reduce(function (min, p) {
+                if (min.ignore) {
+                    return p;
+                }
+                else if (p.ignore) {
+                    return min;
+                }
+                else {
+                    return min.diff < p.diff ? min : p;
+                }
+            }, this._faceVectors[0]);
+        };
+        AttachToBoxBehavior.prototype._lookAtToRef = function (pos, up, ref) {
+            if (up === void 0) { up = new BABYLON.Vector3(0, 1, 0); }
+            BABYLON.Matrix.LookAtLHToRef(this._zeroVector, pos, up, this._lookAtTmpMatrix);
+            this._lookAtTmpMatrix.invert();
+            BABYLON.Quaternion.FromRotationMatrixToRef(this._lookAtTmpMatrix, ref);
+        };
+        /**
+         * Attaches the AttachToBoxBehavior to the passed in mesh
+         * @param target The mesh that the specified node will be attached to
+         */
+        AttachToBoxBehavior.prototype.attach = function (target) {
+            var _this = this;
+            this._target = target;
+            this._scene = this._target.getScene();
+            // Every frame, update the app bars position
+            this._onRenderObserver = this._scene.onBeforeRenderObservable.add(function () {
+                if (!_this._scene.activeCamera) {
+                    return;
+                }
+                // Find the face closest to the cameras position
+                var cameraPos = _this._scene.activeCamera.position;
+                if (_this._scene.activeCamera.devicePosition) {
+                    cameraPos = _this._scene.activeCamera.devicePosition;
+                }
+                var facing = _this._closestFace(cameraPos.subtract(target.position));
+                if (_this._scene.activeCamera.leftCamera) {
+                    _this._scene.activeCamera.leftCamera.computeWorldMatrix().getRotationMatrixToRef(_this._tmpMatrix);
+                }
+                else {
+                    _this._scene.activeCamera.computeWorldMatrix().getRotationMatrixToRef(_this._tmpMatrix);
+                }
+                // Get camera up direction
+                BABYLON.Vector3.TransformCoordinatesToRef(BABYLON.Vector3.Up(), _this._tmpMatrix, _this._tmpVector);
+                // Ignore faces to not select a parrelel face for the up vector of the UI
+                _this._faceVectors.forEach(function (v) {
+                    if (facing.direction.x && v.direction.x) {
+                        v.ignore = true;
+                    }
+                    if (facing.direction.y && v.direction.y) {
+                        v.ignore = true;
+                    }
+                    if (facing.direction.z && v.direction.z) {
+                        v.ignore = true;
+                    }
+                });
+                var facingUp = _this._closestFace(_this._tmpVector);
+                // Unignore faces
+                _this._faceVectors.forEach(function (v) {
+                    v.ignore = false;
+                });
+                // Position the app bar on that face
+                _this.ui.position.copyFrom(target.position);
+                if (facing.direction.x) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.x / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                if (facing.direction.y) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.y / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                if (facing.direction.z) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.z / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                // Rotate to be oriented properly to the camera
+                if (!_this.ui.rotationQuaternion) {
+                    _this.ui.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(_this.ui.rotation.y, _this.ui.rotation.x, _this.ui.rotation.z);
+                }
+                facing.rotatedDirection.scaleToRef(-1, _this._tmpVector);
+                _this._lookAtToRef(_this._tmpVector, facingUp.rotatedDirection, _this.ui.rotationQuaternion);
+                // Place ui the correct distance from the bottom of the mesh
+                if (facingUp.direction.x) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.x / 2, _this._tmpVector);
+                }
+                if (facingUp.direction.y) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.y / 2, _this._tmpVector);
+                }
+                if (facingUp.direction.z) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.z / 2, _this._tmpVector);
+                }
+                _this.ui.position.addInPlace(_this._tmpVector);
+            });
+        };
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        AttachToBoxBehavior.prototype.detach = function () {
+            this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
+        };
+        return AttachToBoxBehavior;
+    }());
+    BABYLON.AttachToBoxBehavior = AttachToBoxBehavior;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.attachToBoxBehavior.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      */
      */
     var Gizmo = /** @class */ (function () {
     var Gizmo = /** @class */ (function () {

+ 201 - 2
dist/preview release/babylon.no-module.max.js

@@ -9437,6 +9437,10 @@ var BABYLON;
          * @param fileName defines the name of the downloaded file
          * @param fileName defines the name of the downloaded file
          */
          */
         Tools.Download = function (blob, fileName) {
         Tools.Download = function (blob, fileName) {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
             var url = window.URL.createObjectURL(blob);
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             var a = document.createElement("a");
             document.body.appendChild(a);
             document.body.appendChild(a);
@@ -24142,6 +24146,8 @@ var BABYLON;
              */
              */
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._activeMeshesFrozen = false;
             _this._activeMeshesFrozen = false;
+            /** @hidden */
+            _this._allowPostProcessClear = true;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine.scenes.push(_this);
             _this._engine.scenes.push(_this);
@@ -67129,7 +67135,7 @@ var BABYLON;
             }
             }
             this.onActivateObservable.notifyObservers(camera);
             this.onActivateObservable.notifyObservers(camera);
             // Clear
             // Clear
-            if (this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
+            if (scene._allowPostProcessClear && this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
             }
             }
             if (this._reusable) {
             if (this._reusable) {
@@ -89249,6 +89255,7 @@ var BABYLON;
             this.onPointerOutObservable = new BABYLON.Observable();
             this.onPointerOutObservable = new BABYLON.Observable();
             // 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._allowPostProcessClear = false;
             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();
@@ -89366,7 +89373,27 @@ var BABYLON;
          */
          */
         UtilityLayerRenderer.prototype.render = function () {
         UtilityLayerRenderer.prototype.render = function () {
             this._updateCamera();
             this._updateCamera();
-            this.utilityLayerScene.render(false);
+            if (this.utilityLayerScene.activeCamera) {
+                // Set the camera's scene to utility layers scene
+                var oldScene = this.utilityLayerScene.activeCamera.getScene();
+                var camera = this.utilityLayerScene.activeCamera;
+                camera._scene = this.utilityLayerScene;
+                if (camera.leftCamera) {
+                    camera.leftCamera._scene = this.utilityLayerScene;
+                }
+                if (camera.rightCamera) {
+                    camera.rightCamera._scene = this.utilityLayerScene;
+                }
+                this.utilityLayerScene.render(false);
+                // Reset camera's scene back to original
+                camera._scene = oldScene;
+                if (camera.leftCamera) {
+                    camera.leftCamera._scene = oldScene;
+                }
+                if (camera.rightCamera) {
+                    camera.rightCamera._scene = oldScene;
+                }
+            }
         };
         };
         /**
         /**
          * Disposes of the renderer
          * Disposes of the renderer
@@ -89972,6 +89999,178 @@ var BABYLON;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     /**
     /**
+     * @hidden
+     */
+    var FaceDirectionInfo = /** @class */ (function () {
+        function FaceDirectionInfo(direction, rotatedDirection, diff, ignore) {
+            if (rotatedDirection === void 0) { rotatedDirection = new BABYLON.Vector3(); }
+            if (diff === void 0) { diff = 0; }
+            if (ignore === void 0) { ignore = false; }
+            this.direction = direction;
+            this.rotatedDirection = rotatedDirection;
+            this.diff = diff;
+            this.ignore = ignore;
+        }
+        return FaceDirectionInfo;
+    }());
+    /**
+     * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+     */
+    var AttachToBoxBehavior = /** @class */ (function () {
+        /**
+         * Creates the AttachToBoxBehavior, used to attach UI to the closest face of the box to a camera
+         * @param ui The transform node that should be attched to the mesh
+         */
+        function AttachToBoxBehavior(ui) {
+            this.ui = ui;
+            /**
+             *  The name of the behavior
+             */
+            this.name = "AttachToBoxBehavior";
+            /**
+             * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
+             */
+            this.distanceAwayFromFace = 0.15;
+            /**
+             * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
+             */
+            this.distanceAwayFromBottomOfFace = 0.15;
+            this._faceVectors = [new FaceDirectionInfo(BABYLON.Vector3.Up()), new FaceDirectionInfo(BABYLON.Vector3.Down()), new FaceDirectionInfo(BABYLON.Vector3.Left()), new FaceDirectionInfo(BABYLON.Vector3.Right()), new FaceDirectionInfo(BABYLON.Vector3.Forward()), new FaceDirectionInfo(BABYLON.Vector3.Forward().scaleInPlace(-1))];
+            this._tmpMatrix = new BABYLON.Matrix();
+            this._tmpVector = new BABYLON.Vector3();
+            this._zeroVector = BABYLON.Vector3.Zero();
+            this._lookAtTmpMatrix = new BABYLON.Matrix();
+            /* Does nothing */
+        }
+        /**
+         *  Initializes the behavior
+         */
+        AttachToBoxBehavior.prototype.init = function () {
+            /* Does nothing */
+        };
+        AttachToBoxBehavior.prototype._closestFace = function (targetDirection) {
+            var _this = this;
+            // Go over each face and calculate the angle between the face's normal and targetDirection
+            this._faceVectors.forEach(function (v) {
+                if (!_this._target.rotationQuaternion) {
+                    _this._target.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(_this._target.rotation.y, _this._target.rotation.x, _this._target.rotation.z);
+                }
+                _this._target.rotationQuaternion.toRotationMatrix(_this._tmpMatrix);
+                BABYLON.Vector3.TransformCoordinatesToRef(v.direction, _this._tmpMatrix, v.rotatedDirection);
+                v.diff = BABYLON.Vector3.GetAngleBetweenVectors(v.rotatedDirection, targetDirection, BABYLON.Vector3.Cross(v.rotatedDirection, targetDirection));
+            });
+            // Return the face information of the one with the normal closeset to target direction
+            return this._faceVectors.reduce(function (min, p) {
+                if (min.ignore) {
+                    return p;
+                }
+                else if (p.ignore) {
+                    return min;
+                }
+                else {
+                    return min.diff < p.diff ? min : p;
+                }
+            }, this._faceVectors[0]);
+        };
+        AttachToBoxBehavior.prototype._lookAtToRef = function (pos, up, ref) {
+            if (up === void 0) { up = new BABYLON.Vector3(0, 1, 0); }
+            BABYLON.Matrix.LookAtLHToRef(this._zeroVector, pos, up, this._lookAtTmpMatrix);
+            this._lookAtTmpMatrix.invert();
+            BABYLON.Quaternion.FromRotationMatrixToRef(this._lookAtTmpMatrix, ref);
+        };
+        /**
+         * Attaches the AttachToBoxBehavior to the passed in mesh
+         * @param target The mesh that the specified node will be attached to
+         */
+        AttachToBoxBehavior.prototype.attach = function (target) {
+            var _this = this;
+            this._target = target;
+            this._scene = this._target.getScene();
+            // Every frame, update the app bars position
+            this._onRenderObserver = this._scene.onBeforeRenderObservable.add(function () {
+                if (!_this._scene.activeCamera) {
+                    return;
+                }
+                // Find the face closest to the cameras position
+                var cameraPos = _this._scene.activeCamera.position;
+                if (_this._scene.activeCamera.devicePosition) {
+                    cameraPos = _this._scene.activeCamera.devicePosition;
+                }
+                var facing = _this._closestFace(cameraPos.subtract(target.position));
+                if (_this._scene.activeCamera.leftCamera) {
+                    _this._scene.activeCamera.leftCamera.computeWorldMatrix().getRotationMatrixToRef(_this._tmpMatrix);
+                }
+                else {
+                    _this._scene.activeCamera.computeWorldMatrix().getRotationMatrixToRef(_this._tmpMatrix);
+                }
+                // Get camera up direction
+                BABYLON.Vector3.TransformCoordinatesToRef(BABYLON.Vector3.Up(), _this._tmpMatrix, _this._tmpVector);
+                // Ignore faces to not select a parrelel face for the up vector of the UI
+                _this._faceVectors.forEach(function (v) {
+                    if (facing.direction.x && v.direction.x) {
+                        v.ignore = true;
+                    }
+                    if (facing.direction.y && v.direction.y) {
+                        v.ignore = true;
+                    }
+                    if (facing.direction.z && v.direction.z) {
+                        v.ignore = true;
+                    }
+                });
+                var facingUp = _this._closestFace(_this._tmpVector);
+                // Unignore faces
+                _this._faceVectors.forEach(function (v) {
+                    v.ignore = false;
+                });
+                // Position the app bar on that face
+                _this.ui.position.copyFrom(target.position);
+                if (facing.direction.x) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.x / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                if (facing.direction.y) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.y / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                if (facing.direction.z) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.z / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                // Rotate to be oriented properly to the camera
+                if (!_this.ui.rotationQuaternion) {
+                    _this.ui.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(_this.ui.rotation.y, _this.ui.rotation.x, _this.ui.rotation.z);
+                }
+                facing.rotatedDirection.scaleToRef(-1, _this._tmpVector);
+                _this._lookAtToRef(_this._tmpVector, facingUp.rotatedDirection, _this.ui.rotationQuaternion);
+                // Place ui the correct distance from the bottom of the mesh
+                if (facingUp.direction.x) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.x / 2, _this._tmpVector);
+                }
+                if (facingUp.direction.y) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.y / 2, _this._tmpVector);
+                }
+                if (facingUp.direction.z) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.z / 2, _this._tmpVector);
+                }
+                _this.ui.position.addInPlace(_this._tmpVector);
+            });
+        };
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        AttachToBoxBehavior.prototype.detach = function () {
+            this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
+        };
+        return AttachToBoxBehavior;
+    }());
+    BABYLON.AttachToBoxBehavior = AttachToBoxBehavior;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.attachToBoxBehavior.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      */
      */
     var Gizmo = /** @class */ (function () {
     var Gizmo = /** @class */ (function () {

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


File diff suppressed because it is too large
+ 203 - 4
dist/preview release/es6.js


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


+ 201 - 2
dist/preview release/viewer/babylon.viewer.max.js

@@ -9558,6 +9558,10 @@ var BABYLON;
          * @param fileName defines the name of the downloaded file
          * @param fileName defines the name of the downloaded file
          */
          */
         Tools.Download = function (blob, fileName) {
         Tools.Download = function (blob, fileName) {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
             var url = window.URL.createObjectURL(blob);
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             var a = document.createElement("a");
             document.body.appendChild(a);
             document.body.appendChild(a);
@@ -24263,6 +24267,8 @@ var BABYLON;
              */
              */
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._activeMeshesFrozen = false;
             _this._activeMeshesFrozen = false;
+            /** @hidden */
+            _this._allowPostProcessClear = true;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine.scenes.push(_this);
             _this._engine.scenes.push(_this);
@@ -67250,7 +67256,7 @@ var BABYLON;
             }
             }
             this.onActivateObservable.notifyObservers(camera);
             this.onActivateObservable.notifyObservers(camera);
             // Clear
             // Clear
-            if (this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
+            if (scene._allowPostProcessClear && this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
             }
             }
             if (this._reusable) {
             if (this._reusable) {
@@ -89370,6 +89376,7 @@ var BABYLON;
             this.onPointerOutObservable = new BABYLON.Observable();
             this.onPointerOutObservable = new BABYLON.Observable();
             // 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._allowPostProcessClear = false;
             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();
@@ -89487,7 +89494,27 @@ var BABYLON;
          */
          */
         UtilityLayerRenderer.prototype.render = function () {
         UtilityLayerRenderer.prototype.render = function () {
             this._updateCamera();
             this._updateCamera();
-            this.utilityLayerScene.render(false);
+            if (this.utilityLayerScene.activeCamera) {
+                // Set the camera's scene to utility layers scene
+                var oldScene = this.utilityLayerScene.activeCamera.getScene();
+                var camera = this.utilityLayerScene.activeCamera;
+                camera._scene = this.utilityLayerScene;
+                if (camera.leftCamera) {
+                    camera.leftCamera._scene = this.utilityLayerScene;
+                }
+                if (camera.rightCamera) {
+                    camera.rightCamera._scene = this.utilityLayerScene;
+                }
+                this.utilityLayerScene.render(false);
+                // Reset camera's scene back to original
+                camera._scene = oldScene;
+                if (camera.leftCamera) {
+                    camera.leftCamera._scene = oldScene;
+                }
+                if (camera.rightCamera) {
+                    camera.rightCamera._scene = oldScene;
+                }
+            }
         };
         };
         /**
         /**
          * Disposes of the renderer
          * Disposes of the renderer
@@ -90093,6 +90120,178 @@ var BABYLON;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     /**
     /**
+     * @hidden
+     */
+    var FaceDirectionInfo = /** @class */ (function () {
+        function FaceDirectionInfo(direction, rotatedDirection, diff, ignore) {
+            if (rotatedDirection === void 0) { rotatedDirection = new BABYLON.Vector3(); }
+            if (diff === void 0) { diff = 0; }
+            if (ignore === void 0) { ignore = false; }
+            this.direction = direction;
+            this.rotatedDirection = rotatedDirection;
+            this.diff = diff;
+            this.ignore = ignore;
+        }
+        return FaceDirectionInfo;
+    }());
+    /**
+     * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+     */
+    var AttachToBoxBehavior = /** @class */ (function () {
+        /**
+         * Creates the AttachToBoxBehavior, used to attach UI to the closest face of the box to a camera
+         * @param ui The transform node that should be attched to the mesh
+         */
+        function AttachToBoxBehavior(ui) {
+            this.ui = ui;
+            /**
+             *  The name of the behavior
+             */
+            this.name = "AttachToBoxBehavior";
+            /**
+             * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
+             */
+            this.distanceAwayFromFace = 0.15;
+            /**
+             * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
+             */
+            this.distanceAwayFromBottomOfFace = 0.15;
+            this._faceVectors = [new FaceDirectionInfo(BABYLON.Vector3.Up()), new FaceDirectionInfo(BABYLON.Vector3.Down()), new FaceDirectionInfo(BABYLON.Vector3.Left()), new FaceDirectionInfo(BABYLON.Vector3.Right()), new FaceDirectionInfo(BABYLON.Vector3.Forward()), new FaceDirectionInfo(BABYLON.Vector3.Forward().scaleInPlace(-1))];
+            this._tmpMatrix = new BABYLON.Matrix();
+            this._tmpVector = new BABYLON.Vector3();
+            this._zeroVector = BABYLON.Vector3.Zero();
+            this._lookAtTmpMatrix = new BABYLON.Matrix();
+            /* Does nothing */
+        }
+        /**
+         *  Initializes the behavior
+         */
+        AttachToBoxBehavior.prototype.init = function () {
+            /* Does nothing */
+        };
+        AttachToBoxBehavior.prototype._closestFace = function (targetDirection) {
+            var _this = this;
+            // Go over each face and calculate the angle between the face's normal and targetDirection
+            this._faceVectors.forEach(function (v) {
+                if (!_this._target.rotationQuaternion) {
+                    _this._target.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(_this._target.rotation.y, _this._target.rotation.x, _this._target.rotation.z);
+                }
+                _this._target.rotationQuaternion.toRotationMatrix(_this._tmpMatrix);
+                BABYLON.Vector3.TransformCoordinatesToRef(v.direction, _this._tmpMatrix, v.rotatedDirection);
+                v.diff = BABYLON.Vector3.GetAngleBetweenVectors(v.rotatedDirection, targetDirection, BABYLON.Vector3.Cross(v.rotatedDirection, targetDirection));
+            });
+            // Return the face information of the one with the normal closeset to target direction
+            return this._faceVectors.reduce(function (min, p) {
+                if (min.ignore) {
+                    return p;
+                }
+                else if (p.ignore) {
+                    return min;
+                }
+                else {
+                    return min.diff < p.diff ? min : p;
+                }
+            }, this._faceVectors[0]);
+        };
+        AttachToBoxBehavior.prototype._lookAtToRef = function (pos, up, ref) {
+            if (up === void 0) { up = new BABYLON.Vector3(0, 1, 0); }
+            BABYLON.Matrix.LookAtLHToRef(this._zeroVector, pos, up, this._lookAtTmpMatrix);
+            this._lookAtTmpMatrix.invert();
+            BABYLON.Quaternion.FromRotationMatrixToRef(this._lookAtTmpMatrix, ref);
+        };
+        /**
+         * Attaches the AttachToBoxBehavior to the passed in mesh
+         * @param target The mesh that the specified node will be attached to
+         */
+        AttachToBoxBehavior.prototype.attach = function (target) {
+            var _this = this;
+            this._target = target;
+            this._scene = this._target.getScene();
+            // Every frame, update the app bars position
+            this._onRenderObserver = this._scene.onBeforeRenderObservable.add(function () {
+                if (!_this._scene.activeCamera) {
+                    return;
+                }
+                // Find the face closest to the cameras position
+                var cameraPos = _this._scene.activeCamera.position;
+                if (_this._scene.activeCamera.devicePosition) {
+                    cameraPos = _this._scene.activeCamera.devicePosition;
+                }
+                var facing = _this._closestFace(cameraPos.subtract(target.position));
+                if (_this._scene.activeCamera.leftCamera) {
+                    _this._scene.activeCamera.leftCamera.computeWorldMatrix().getRotationMatrixToRef(_this._tmpMatrix);
+                }
+                else {
+                    _this._scene.activeCamera.computeWorldMatrix().getRotationMatrixToRef(_this._tmpMatrix);
+                }
+                // Get camera up direction
+                BABYLON.Vector3.TransformCoordinatesToRef(BABYLON.Vector3.Up(), _this._tmpMatrix, _this._tmpVector);
+                // Ignore faces to not select a parrelel face for the up vector of the UI
+                _this._faceVectors.forEach(function (v) {
+                    if (facing.direction.x && v.direction.x) {
+                        v.ignore = true;
+                    }
+                    if (facing.direction.y && v.direction.y) {
+                        v.ignore = true;
+                    }
+                    if (facing.direction.z && v.direction.z) {
+                        v.ignore = true;
+                    }
+                });
+                var facingUp = _this._closestFace(_this._tmpVector);
+                // Unignore faces
+                _this._faceVectors.forEach(function (v) {
+                    v.ignore = false;
+                });
+                // Position the app bar on that face
+                _this.ui.position.copyFrom(target.position);
+                if (facing.direction.x) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.x / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                if (facing.direction.y) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.y / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                if (facing.direction.z) {
+                    facing.rotatedDirection.scaleToRef((target.scaling.z / 2) + _this.distanceAwayFromFace, _this._tmpVector);
+                    _this.ui.position.addInPlace(_this._tmpVector);
+                }
+                // Rotate to be oriented properly to the camera
+                if (!_this.ui.rotationQuaternion) {
+                    _this.ui.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(_this.ui.rotation.y, _this.ui.rotation.x, _this.ui.rotation.z);
+                }
+                facing.rotatedDirection.scaleToRef(-1, _this._tmpVector);
+                _this._lookAtToRef(_this._tmpVector, facingUp.rotatedDirection, _this.ui.rotationQuaternion);
+                // Place ui the correct distance from the bottom of the mesh
+                if (facingUp.direction.x) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.x / 2, _this._tmpVector);
+                }
+                if (facingUp.direction.y) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.y / 2, _this._tmpVector);
+                }
+                if (facingUp.direction.z) {
+                    _this.ui.up.scaleToRef(_this.distanceAwayFromBottomOfFace - target.scaling.z / 2, _this._tmpVector);
+                }
+                _this.ui.position.addInPlace(_this._tmpVector);
+            });
+        };
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        AttachToBoxBehavior.prototype.detach = function () {
+            this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
+        };
+        return AttachToBoxBehavior;
+    }());
+    BABYLON.AttachToBoxBehavior = AttachToBoxBehavior;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.attachToBoxBehavior.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      */
      */
     var Gizmo = /** @class */ (function () {
     var Gizmo = /** @class */ (function () {