Преглед на файлове

Merge remote-tracking branch 'BabylonJS/master' into gui-module

# Conflicts:
#	gui/src/3D/controls/container3D.ts
#	gui/src/3D/controls/volumeBasedPanel.ts
Raanan Weber преди 7 години
родител
ревизия
ea34529702
променени са 100 файла, в които са добавени 15115 реда и са изтрити 13583 реда
  1. 4 2
      .travis.yml
  2. 5082 4998
      Playground/babylon.d.txt
  3. 2 1
      Tools/Gulp/config.json
  4. 35 0
      Tools/Gulp/gulpfile.js
  5. BIN
      Viewer/tests/validation/ReferenceImages/Control.png
  6. BIN
      Viewer/tests/validation/ReferenceImages/ControlDefault.png
  7. BIN
      Viewer/tests/validation/ReferenceImages/Diffuse.png
  8. BIN
      Viewer/tests/validation/ReferenceImages/Emissive.png
  9. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv0-0.png
  10. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv0-100.png
  11. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv0-50.png
  12. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv100-0.png
  13. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv100-100.png
  14. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv100-50.png
  15. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv50-0.png
  16. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv50-100.png
  17. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv50-50.png
  18. BIN
      Viewer/tests/validation/ReferenceImages/Specular0-0.png
  19. BIN
      Viewer/tests/validation/ReferenceImages/Specular0-100.png
  20. BIN
      Viewer/tests/validation/ReferenceImages/Specular0-25.png
  21. BIN
      Viewer/tests/validation/ReferenceImages/Specular0-50.png
  22. BIN
      Viewer/tests/validation/ReferenceImages/Specular0-75.png
  23. BIN
      Viewer/tests/validation/ReferenceImages/Specular100-0.png
  24. BIN
      Viewer/tests/validation/ReferenceImages/Specular100-100.png
  25. BIN
      Viewer/tests/validation/ReferenceImages/Specular100-25.png
  26. BIN
      Viewer/tests/validation/ReferenceImages/Specular100-50.png
  27. BIN
      Viewer/tests/validation/ReferenceImages/Specular100-75.png
  28. BIN
      Viewer/tests/validation/ReferenceImages/Specular25-0.png
  29. BIN
      Viewer/tests/validation/ReferenceImages/Specular25-100.png
  30. BIN
      Viewer/tests/validation/ReferenceImages/Specular25-25.png
  31. BIN
      Viewer/tests/validation/ReferenceImages/Specular25-50.png
  32. BIN
      Viewer/tests/validation/ReferenceImages/Specular25-75.png
  33. BIN
      Viewer/tests/validation/ReferenceImages/Specular50-0.png
  34. BIN
      Viewer/tests/validation/ReferenceImages/Specular50-100.png
  35. BIN
      Viewer/tests/validation/ReferenceImages/Specular50-25.png
  36. BIN
      Viewer/tests/validation/ReferenceImages/Specular50-50.png
  37. BIN
      Viewer/tests/validation/ReferenceImages/Specular50-75.png
  38. BIN
      Viewer/tests/validation/ReferenceImages/Specular75-0.png
  39. BIN
      Viewer/tests/validation/ReferenceImages/Specular75-100.png
  40. BIN
      Viewer/tests/validation/ReferenceImages/Specular75-25.png
  41. BIN
      Viewer/tests/validation/ReferenceImages/Specular75-50.png
  42. BIN
      Viewer/tests/validation/ReferenceImages/Specular75-75.png
  43. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv0-0.png
  44. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv0-100.png
  45. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv0-50.png
  46. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv100-0.png
  47. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv100-100.png
  48. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv100-50.png
  49. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv50-0.png
  50. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv50-100.png
  51. BIN
      Viewer/tests/validation/ReferenceImages/SpecularEnv50-50.png
  52. 44 8
      Viewer/tests/validation/config.json
  53. 1 1
      Viewer/tests/validation/integration.js
  54. 14 20
      Viewer/tests/validation/karma.conf.browserstack.js
  55. 39 40
      Viewer/tests/validation/validation.js
  56. 8102 8015
      dist/preview release/babylon.d.ts
  57. 26 26
      dist/preview release/babylon.js
  58. 319 69
      dist/preview release/babylon.max.js
  59. 319 69
      dist/preview release/babylon.no-module.max.js
  60. 26 26
      dist/preview release/babylon.worker.js
  61. 321 71
      dist/preview release/es6.js
  62. 1 1
      dist/preview release/glTF2Interface/package.json
  63. 6 1
      dist/preview release/gui/babylon.gui.d.ts
  64. 16 4
      dist/preview release/gui/babylon.gui.js
  65. 1 1
      dist/preview release/gui/babylon.gui.min.js
  66. 6 1
      dist/preview release/gui/babylon.gui.module.d.ts
  67. 1 1
      dist/preview release/gui/package.json
  68. 1 1
      dist/preview release/inspector/package.json
  69. 2 2
      dist/preview release/loaders/package.json
  70. 1 1
      dist/preview release/materialsLibrary/package.json
  71. 1 1
      dist/preview release/postProcessesLibrary/package.json
  72. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  73. 6 4
      dist/preview release/serializers/babylon.glTF2Serializer.js
  74. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  75. 6 4
      dist/preview release/serializers/babylonjs.serializers.js
  76. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  77. 2 2
      dist/preview release/serializers/package.json
  78. 34 34
      dist/preview release/viewer/babylon.viewer.js
  79. 311 69
      dist/preview release/viewer/babylon.viewer.max.js
  80. 4 2
      dist/preview release/what's new.md
  81. 10 1
      gui/src/3D/controls/container3D.ts
  82. 18 11
      gui/src/3D/controls/volumeBasedPanel.ts
  83. 1 1
      package.json
  84. 10 8
      serializers/src/glTF/2.0/babylon.glTFUtilities.ts
  85. 166 0
      src/Behaviors/Mesh/babylon.attachToBoxBehavior.ts
  86. 24 0
      src/Behaviors/Mesh/babylon.pointerDragBehavior.ts
  87. 20 1
      src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts
  88. 1 1
      src/Engine/babylon.engine.ts
  89. 6 0
      src/Helpers/babylon.photoDome.ts
  90. 6 0
      src/Helpers/babylon.videoDome.ts
  91. 2 0
      src/Materials/Textures/babylon.renderTargetTexture.ts
  92. 9 0
      src/Mesh/babylon.linesMesh.ts
  93. 1 1
      src/PostProcess/babylon.postProcess.ts
  94. 50 46
      src/Rendering/babylon.renderingManager.ts
  95. 25 1
      src/Rendering/babylon.utilityLayerRenderer.ts
  96. 5 0
      src/Tools/babylon.tools.ts
  97. 2 2
      src/babylon.node.ts
  98. 23 32
      src/babylon.scene.ts
  99. BIN
      tests/validation/ReferenceImages/spherepanel.png
  100. 0 0
      tests/validation/config.json

Файловите разлики са ограничени, защото са твърде много
+ 4 - 2
.travis.yml


Файловите разлики са ограничени, защото са твърде много
+ 5082 - 4998
Playground/babylon.d.txt


+ 2 - 1
Tools/Gulp/config.json

@@ -340,7 +340,8 @@
             "files": [
                 "../../src/Behaviors/Mesh/babylon.pointerDragBehavior.js",
                 "../../src/Behaviors/Mesh/babylon.multiPointerScaleBehavior.js",
-                "../../src/Behaviors/Mesh/babylon.sixDofDragBehavior.js"
+                "../../src/Behaviors/Mesh/babylon.sixDofDragBehavior.js",
+                "../../src/Behaviors/Mesh/babylon.attachToBoxBehavior.js"
             ],
             "dependUpon": [
                 "behaviors"

+ 35 - 0
Tools/Gulp/gulpfile.js

@@ -1134,6 +1134,41 @@ gulp.task("tests-viewer-validation-karma", ["tests-viewer-validation-transpile"]
 });
 
 /**
+ * Launches the KARMA validation tests in ff or virtual screen ff on travis for a quick analysis during the build.
+ * (Can only be launch on any branches.)
+ */
+gulp.task("tests-viewer-validation-virtualscreen", ["tests-viewer-validation-transpile"], function (done) {
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../Viewer/tests/validation/karma.conf.js",
+        singleRun: true,
+        browsers: ['Firefox']
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});
+
+/**
+ * Launches the KARMA validation tests in browser stack for remote and cross devices validation tests.
+ * (Can only be launch from secure branches.)
+ */
+gulp.task("tests-viewer-validation-browserstack", ["tests-viewer-validation-transpile"], function (done) {
+    if (!process.env.BROWSER_STACK_USERNAME) {
+        done();
+        return;
+    }
+
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../Viewer/tests/validation/karma.conf.browserstack.js",
+        singleRun: true
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});
+
+
+/**
  * Transpiles viewer typescript unit tests. 
  */
 gulp.task("tests-viewer-validation-transpile", function (done) {

BIN
Viewer/tests/validation/ReferenceImages/Control.png


BIN
Viewer/tests/validation/ReferenceImages/ControlDefault.png


BIN
Viewer/tests/validation/ReferenceImages/Diffuse.png


BIN
Viewer/tests/validation/ReferenceImages/Emissive.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv0-0.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv0-100.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv0-50.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv100-0.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv100-100.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv100-50.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv50-0.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv50-100.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv50-50.png


BIN
Viewer/tests/validation/ReferenceImages/Specular0-0.png


BIN
Viewer/tests/validation/ReferenceImages/Specular0-100.png


BIN
Viewer/tests/validation/ReferenceImages/Specular0-25.png


BIN
Viewer/tests/validation/ReferenceImages/Specular0-50.png


BIN
Viewer/tests/validation/ReferenceImages/Specular0-75.png


BIN
Viewer/tests/validation/ReferenceImages/Specular100-0.png


BIN
Viewer/tests/validation/ReferenceImages/Specular100-100.png


BIN
Viewer/tests/validation/ReferenceImages/Specular100-25.png


BIN
Viewer/tests/validation/ReferenceImages/Specular100-50.png


BIN
Viewer/tests/validation/ReferenceImages/Specular100-75.png


BIN
Viewer/tests/validation/ReferenceImages/Specular25-0.png


BIN
Viewer/tests/validation/ReferenceImages/Specular25-100.png


BIN
Viewer/tests/validation/ReferenceImages/Specular25-25.png


BIN
Viewer/tests/validation/ReferenceImages/Specular25-50.png


BIN
Viewer/tests/validation/ReferenceImages/Specular25-75.png


BIN
Viewer/tests/validation/ReferenceImages/Specular50-0.png


BIN
Viewer/tests/validation/ReferenceImages/Specular50-100.png


BIN
Viewer/tests/validation/ReferenceImages/Specular50-25.png


BIN
Viewer/tests/validation/ReferenceImages/Specular50-50.png


BIN
Viewer/tests/validation/ReferenceImages/Specular50-75.png


BIN
Viewer/tests/validation/ReferenceImages/Specular75-0.png


BIN
Viewer/tests/validation/ReferenceImages/Specular75-100.png


BIN
Viewer/tests/validation/ReferenceImages/Specular75-25.png


BIN
Viewer/tests/validation/ReferenceImages/Specular75-50.png


BIN
Viewer/tests/validation/ReferenceImages/Specular75-75.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv0-0.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv0-100.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv0-50.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv100-0.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv100-100.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv100-50.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv50-0.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv50-100.png


BIN
Viewer/tests/validation/ReferenceImages/SpecularEnv50-50.png


+ 44 - 8
Viewer/tests/validation/config.json

@@ -2,10 +2,46 @@
     "root": "",
     "tests": [
         {
+            "title": "Diffuse",
+            "createMesh": true,
+            "createMaterial": true,
+            "configuration": {
+                "extends": "extended",
+                "castShadow": true,
+                "model": {
+                    "material": {
+                        "albedoColor": {
+                            "r": 0,
+                            "g": 0,
+                            "b": 1
+                        },
+                        "reflectivityColor": {
+                            "r": 0,
+                            "g": 0,
+                            "b": 0
+                        },
+                        "microSurface": 0
+                    }
+                }
+            },
+            "referenceImage": "Diffuse.png"
+        },
+        {
+            "title": "BrainStem Environment",
+            "configuration": {
+                "extends": "extended"
+            },
+            "model": "/dist/assets/BrainStem/BrainStem.gltf",
+            "referenceImage": "BrainStemEnv.png",
+            "enableEnvironment": true
+        }
+    ],
+    "allTests": [
+        {
             "title": "Control Default",
             "createMesh": true,
             "configuration": {
-                "extends": "default"
+                "extends": "minimal"
             },
             "referenceImage": "ControlDefault.png"
         },
@@ -254,11 +290,11 @@
             "enableEnvironment": true
         },
         {
-            "title": "Specular {{glos * 25}} - {{spec * 25}}",
+            "title": "Specular {{glos * 50}} - {{spec * 50}}",
             "createMesh": true,
             "createMaterial": true,
             "repeatVariables": "glos,spec",
-            "repeatTimes": "5,5",
+            "repeatTimes": "3,3",
             "configuration": {
                 "extends": "extended",
                 "castShadow": true,
@@ -270,15 +306,15 @@
                             "b": 1
                         },
                         "reflectivityColor": {
-                            "r": "{{spec / 4}}",
-                            "g": "{{spec / 4}}",
-                            "b": "{{spec / 4}}"
+                            "r": "{{spec / 2}}",
+                            "g": "{{spec / 2}}",
+                            "b": "{{spec / 2}}"
                         },
-                        "microSurface": "{{glos / 4}}"
+                        "microSurface": "{{glos / 2}}"
                     }
                 }
             },
-            "referenceImage": "Specular{{glos * 25}}-{{spec * 25}}.png"
+            "referenceImage": "Specular{{glos * 50}}-{{spec * 50}}.png"
         },
         {
             "title": "BrainStem",

+ 1 - 1
Viewer/tests/validation/integration.js

@@ -67,7 +67,7 @@ window.onload = function () {
                                 }
                                 catch (e) {
                                     if (screenshot) {
-                                        console.error(screenshot);
+                                        //console.error(screenshot);
                                     }
                                     deferredDone(e);
                                 }

+ 14 - 20
Viewer/tests/validation/karma.conf.browserstack.js

@@ -9,27 +9,21 @@ module.exports = function (config) {
         browserDisconnectTolerance: 3,
         concurrency: 1,
 
-        urlRoot: '/karma',
+        urlRoot: '/karma/',
 
         frameworks: ['mocha', 'chai', 'sinon'],
 
         files: [
-            './dist/preview release/earcut.min.js',
-            './Tools/DevLoader/BabylonLoader.js',
             './tests/validation/index.css',
             './tests/validation/integration.js',
-            './favicon.ico',
-            { pattern: 'dist/**/*', watched: false, included: false, served: true },
-            { pattern: 'assets/**/*', watched: false, included: false, served: true },
-            { pattern: 'tests/**/*', watched: false, included: false, served: true },
-            { pattern: 'Playground/scenes/**/*', watched: false, included: false, served: true },
-            { pattern: 'Playground/textures/**/*', watched: false, included: false, served: true },
-            { pattern: 'Playground/sounds/**/*', watched: false, included: false, served: true },
-            { pattern: 'Tools/DevLoader/**/*', watched: false, included: false, served: true },            
-            { pattern: 'Tools/Gulp/config.json', watched: false, included: false, served: true },
+            './tests/build/test.js',
+            './tests/validation/validation.js',
+            { pattern: './tests/**/*', watched: false, included: false, served: true },
+            { pattern: './dist/assets/**/*', watched: false, included: false, served: true },
         ],
         proxies: {
-            '/': '/base/'
+            '/tests/': '/base/tests/',
+            '/dist/assets/': '/base//dist/assets/'
         },
 
         port: 1338,
@@ -44,7 +38,7 @@ module.exports = function (config) {
         browserStack: {
             project: 'Babylon JS Validation Tests',
             video: false,
-            debug : 'true',
+            debug: 'true',
             timeout: 1200,
             build: process.env.TRAVIS_BUILD_NUMBER,
             username: process.env.BROWSER_STACK_USERNAME,
@@ -75,16 +69,16 @@ module.exports = function (config) {
             bs_chrome_android: {
                 base: 'BrowserStack',
                 os: 'Android',
-                os_version : '8.0',
-                device : 'Google Pixel',
-                real_mobile : 'true'
+                os_version: '8.0',
+                device: 'Google Pixel',
+                real_mobile: 'true'
             },
             bs_safari_ios: {
                 base: 'BrowserStack',
                 os: 'ios',
-                os_version : '10.3',
-                device : 'iPhone 7',
-                real_mobile : 'true'
+                os_version: '10.3',
+                device: 'iPhone 7',
+                real_mobile: 'true'
             }
         },
         browsers: ['bs_chrome_android'],

+ 39 - 40
Viewer/tests/validation/validation.js

@@ -138,6 +138,8 @@ function evaluate(test, resultCanvas, result, renderImage, index, waitRing, done
 
     var renderB64 = saveRenderImage(renderData, currentViewer.canvas);
     renderImage.src = renderB64;
+
+    // save all reference images
     // downloadDataUrlFromJavascript(test.referenceImage, renderB64)
 
     done(testRes, renderB64);
@@ -151,11 +153,6 @@ function runTest(index, done) {
 
     var test = Object.assign({}, config.tests[index]);
 
-    //process params
-    /*if (params) {
-        processTest(test, "", params);
-    }*/
-
     var container = document.createElement("div");
     container.id = "container#" + index;
     container.className = "container";
@@ -236,35 +233,42 @@ function runTest(index, done) {
 
     // create a new viewer
     currentViewer && currentViewer.dispose();
-    currentViewer = null;
-    currentScene = null;
-    viewerElement.innerHTML = '';
-    currentViewer = new BabylonViewer.DefaultViewer(viewerElement, configuration);
-
-    currentViewer.onInitDoneObservable.add(() => {
-
-        var currentFrame = 0;
-        var waitForFrame = test.waitForFrame || 0;
-
-        if (test.model) {
-            currentViewer.initModel(test.model);
-        } else if (test.createMesh) {
-            prepareMeshForViewer(currentViewer, configuration, test);
-        }
-
-        currentViewer.onModelLoadedObservable.add((model) => {
-            currentViewer.onFrameRenderedObservable.add(() => {
-                if (test.animationTest && !currentFrame) {
-                    model.playAnimation(model.getAnimationNames()[0]);
-                }
-                if (currentFrame === waitForFrame) {
-                    currentViewer.sceneManager.scene.executeWhenReady(() => {
-                        evaluate(test, resultCanvas, result, renderImage, index, waitRing, done);
-                    });
-                }
-                currentFrame++;
+    currentViewer && currentViewer.engine.dispose();
+
+    setTimeout(() => {
+        currentViewer = null;
+        currentScene = null;
+        viewerElement.innerHTML = '';
+        currentViewer = new BabylonViewer.DefaultViewer(viewerElement, configuration);
+
+        currentViewer.onInitDoneObservable.add(() => {
+
+            var currentFrame = 0;
+            var waitForFrame = test.waitForFrame || 0;
+
+            currentViewer.onModelLoadedObservable.add((model) => {
+                console.log("model loaded");
+                currentViewer.onFrameRenderedObservable.add(() => {
+                    console.log("frame rendered", currentFrame, model.meshes.every(m => m.isReady()));
+                    if (test.animationTest && !currentFrame) {
+                        model.playAnimation(model.getAnimationNames()[0]);
+                    }
+                    if (currentFrame === waitForFrame) {
+                        currentViewer.onFrameRenderedObservable.clear();
+                        currentViewer.sceneManager.scene.executeWhenReady(() => {
+                            evaluate(test, resultCanvas, result, renderImage, index, waitRing, done);
+                        });
+                    }
+                    currentFrame++;
+                });
             });
-        })
+
+            if (test.model) {
+                currentViewer.initModel(test.model);
+            } else if (test.createMesh) {
+                prepareMeshForViewer(currentViewer, configuration, test);
+            }
+        });
     });
 }
 
@@ -274,16 +278,11 @@ function prepareMeshForViewer(viewer, configuration, test) {
     let sphereMesh = BABYLON.Mesh.CreateSphere('sphere-' + test.title, 20, 1.0, viewer.sceneManager.scene);
     if (test.createMaterial) {
         let material = new BABYLON.PBRMaterial("sphereMat", viewer.sceneManager.scene);
-        /* material.useAlphaFresnel = material.needAlphaBlending();
-         material.backFaceCulling = material.forceDepthWrite;
-         material.twoSidedLighting = true;
-         material.useSpecularOverAlpha = false;
-         material.useRadianceOverAlpha = false;
-         material.usePhysicalLightFalloff = true;
-         material.forceNormalForward = true;*/
         sphereMesh.material = material;
     }
     meshModel.addMesh(sphereMesh, true);
+    meshModel.rootMesh.position.y = 0.5;
+    console.log("sphere created");
 }
 
 function init() {

Файловите разлики са ограничени, защото са твърде много
+ 8102 - 8015
dist/preview release/babylon.d.ts


Файловите разлики са ограничени, защото са твърде много
+ 26 - 26
dist/preview release/babylon.js


+ 319 - 69
dist/preview release/babylon.max.js

@@ -9470,6 +9470,10 @@ var BABYLON;
          * @param fileName defines the name of the downloaded file
          */
         Tools.Download = function (blob, fileName) {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             document.body.appendChild(a);
@@ -11693,7 +11697,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "3.3.0-alpha.11";
+                return "3.3.0-alpha.12";
             },
             enumerable: true,
             configurable: true
@@ -22838,12 +22842,16 @@ var BABYLON;
 (function (BABYLON) {
     var RenderingManager = /** @class */ (function () {
         function RenderingManager(scene) {
+            /**
+             * Hidden
+             */
+            this._useSceneAutoClearSetup = false;
             this._renderingGroups = new Array();
             this._autoClearDepthStencil = {};
             this._customOpaqueSortCompareFn = {};
             this._customAlphaTestSortCompareFn = {};
             this._customTransparentSortCompareFn = {};
-            this._renderinGroupInfo = null;
+            this._renderingGroupInfo = new BABYLON.RenderingGroupInfo();
             this._scene = scene;
             for (var i = RenderingManager.MIN_RENDERINGGROUPS; i < RenderingManager.MAX_RENDERINGGROUPS; i++) {
                 this._autoClearDepthStencil[i] = { autoClear: true, depth: true, stencil: true };
@@ -22859,17 +22867,10 @@ var BABYLON;
             this._depthStencilBufferAlreadyCleaned = true;
         };
         RenderingManager.prototype.render = function (customRenderFunction, activeMeshes, renderParticles, renderSprites) {
-            // Check if there's at least on observer on the onRenderingGroupObservable and initialize things to fire it
-            var observable = this._scene.onRenderingGroupObservable.hasObservers() ? this._scene.onRenderingGroupObservable : null;
-            var info = null;
-            if (observable) {
-                if (!this._renderinGroupInfo) {
-                    this._renderinGroupInfo = new BABYLON.RenderingGroupInfo();
-                }
-                info = this._renderinGroupInfo;
-                info.scene = this._scene;
-                info.camera = this._scene.activeCamera;
-            }
+            // Update the observable context (not null as it only goes away on dispose)
+            var info = this._renderingGroupInfo;
+            info.scene = this._scene;
+            info.camera = this._scene.activeCamera;
             // Dispatch sprites
             if (renderSprites) {
                 for (var index = 0; index < this._scene.spriteManagers.length; index++) {
@@ -22881,47 +22882,33 @@ var BABYLON;
             for (var index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
                 this._depthStencilBufferAlreadyCleaned = index === RenderingManager.MIN_RENDERINGGROUPS;
                 var renderingGroup = this._renderingGroups[index];
-                if (!renderingGroup && !observable)
+                if (!renderingGroup)
                     continue;
-                var renderingGroupMask = 0;
-                // Fire PRECLEAR stage
-                if (observable && info) {
-                    renderingGroupMask = Math.pow(2, index);
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRECLEAR;
-                    info.renderingGroupId = index;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
+                var renderingGroupMask = Math.pow(2, index);
+                info.renderingGroupId = index;
+                // Before Observable
+                this._scene.onBeforeRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
                 // Clear depth/stencil if needed
                 if (RenderingManager.AUTOCLEAR) {
-                    var autoClear = this._autoClearDepthStencil[index];
+                    var autoClear = this._useSceneAutoClearSetup ?
+                        this._scene.getAutoClearDepthStencilSetup(index) :
+                        this._autoClearDepthStencil[index];
                     if (autoClear && autoClear.autoClear) {
                         this._clearDepthStencilBuffer(autoClear.depth, autoClear.stencil);
                     }
                 }
-                if (observable && info) {
-                    // Fire PREOPAQUE stage
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PREOPAQUE;
-                    observable.notifyObservers(info, renderingGroupMask);
-                    // Fire PRETRANSPARENT stage
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRETRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
-                if (renderingGroup) {
-                    for (var _i = 0, _a = this._scene._beforeRenderingGroupDrawStage; _i < _a.length; _i++) {
-                        var step = _a[_i];
-                        step.action(index);
-                    }
-                    renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
-                    for (var _b = 0, _c = this._scene._afterRenderingGroupDrawStage; _b < _c.length; _b++) {
-                        var step = _c[_b];
-                        step.action(index);
-                    }
+                // Render
+                for (var _i = 0, _a = this._scene._beforeRenderingGroupDrawStage; _i < _a.length; _i++) {
+                    var step = _a[_i];
+                    step.action(index);
                 }
-                // Fire POSTTRANSPARENT stage
-                if (observable && info) {
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_POSTTRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
+                renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
+                for (var _b = 0, _c = this._scene._afterRenderingGroupDrawStage; _b < _c.length; _b++) {
+                    var step = _c[_b];
+                    step.action(index);
                 }
+                // After Observable
+                this._scene.onAfterRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
             }
         };
         RenderingManager.prototype.reset = function () {
@@ -22935,6 +22922,7 @@ var BABYLON;
         RenderingManager.prototype.dispose = function () {
             this.freeRenderingGroups();
             this._renderingGroups.length = 0;
+            this._renderingGroupInfo = null;
         };
         /**
          * Clear the info related to rendering groups preventing retention points during dispose.
@@ -23016,6 +23004,15 @@ var BABYLON;
             };
         };
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        RenderingManager.prototype.getAutoClearDepthStencilSetup = function (index) {
+            return this._autoClearDepthStencil[index];
+        };
+        /**
          * The max id used for rendering groups (not included)
          */
         RenderingManager.MAX_RENDERINGGROUPS = 4;
@@ -23674,26 +23671,6 @@ var BABYLON;
     var RenderingGroupInfo = /** @class */ (function () {
         function RenderingGroupInfo() {
         }
-        /**
-         * Stage corresponding to the very first hook in the renderingGroup phase: before the render buffer may be cleared
-         * This stage will be fired no matter what
-         */
-        RenderingGroupInfo.STAGE_PRECLEAR = 1;
-        /**
-         * Called before opaque object are rendered.
-         * This stage will be fired only if there's 3D Opaque content to render
-         */
-        RenderingGroupInfo.STAGE_PREOPAQUE = 2;
-        /**
-         * Called after the opaque objects are rendered and before the transparent ones
-         * This stage will be fired only if there's 3D transparent content to render
-         */
-        RenderingGroupInfo.STAGE_PRETRANSPARENT = 3;
-        /**
-         * Called after the transparent object are rendered, last hook of the renderingGroup phase
-         * This stage will be fired no matter what
-         */
-        RenderingGroupInfo.STAGE_POSTTRANSPARENT = 4;
         return RenderingGroupInfo;
     }());
     BABYLON.RenderingGroupInfo = RenderingGroupInfo;
@@ -23909,11 +23886,17 @@ var BABYLON;
             */
             _this.onAfterStepObservable = new BABYLON.Observable();
             /**
-             * This Observable will be triggered for each stage of each renderingGroup of each rendered camera.
+             * This Observable will be triggered before rendering each renderingGroup of each rendered camera.
              * The RenderinGroupInfo class contains all the information about the context in which the observable is called
              * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
              */
-            _this.onRenderingGroupObservable = new BABYLON.Observable();
+            _this.onBeforeRenderingGroupObservable = new BABYLON.Observable();
+            /**
+             * This Observable will be triggered after rendering each renderingGroup of each rendered camera.
+             * The RenderinGroupInfo class contains all the information about the context in which the observable is called
+             * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
+             */
+            _this.onAfterRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             _this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
@@ -24175,6 +24158,8 @@ var BABYLON;
              */
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._activeMeshesFrozen = false;
+            /** @hidden */
+            _this._allowPostProcessClear = true;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine.scenes.push(_this);
@@ -28035,6 +28020,8 @@ var BABYLON;
             this.onBeforeAnimationsObservable.clear();
             this.onAfterAnimationsObservable.clear();
             this.onDataLoadedObservable.clear();
+            this.onBeforeRenderingGroupObservable.clear();
+            this.onAfterRenderingGroupObservable.clear();
             this.detachControl();
             // Release sounds & sounds tracks
             if (BABYLON.AudioEngine) {
@@ -28849,6 +28836,15 @@ var BABYLON;
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil, depth, stencil);
         };
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        Scene.prototype.getAutoClearDepthStencilSetup = function (index) {
+            return this._renderingManager.getAutoClearDepthStencilSetup(index);
+        };
+        /**
          * Will flag all materials as dirty to trigger new shader compilation
          * @param flag defines the flag used to specify which material part must be marked as dirty
          * @param predicate If not null, it will be used to specifiy if a material has to be marked as dirty
@@ -58186,9 +58182,15 @@ var BABYLON;
             return "LinesMesh";
         };
         Object.defineProperty(LinesMesh.prototype, "material", {
+            /**
+             * @hidden
+             */
             get: function () {
                 return this._colorShader;
             },
+            /**
+             * @hidden
+             */
             set: function (value) {
                 // Do nothing
             },
@@ -58196,6 +58198,9 @@ var BABYLON;
             configurable: true
         });
         Object.defineProperty(LinesMesh.prototype, "checkCollisions", {
+            /**
+             * @hidden
+             */
             get: function () {
                 return false;
             },
@@ -64975,6 +64980,7 @@ var BABYLON;
             _this._doNotChangeAspectRatio = doNotChangeAspectRatio;
             // Rendering groups
             _this._renderingManager = new BABYLON.RenderingManager(scene);
+            _this._renderingManager._useSceneAutoClearSetup = true;
             if (isMulti) {
                 return _this;
             }
@@ -65475,6 +65481,7 @@ var BABYLON;
          */
         RenderTargetTexture.prototype.setRenderingAutoClearDepthStencil = function (renderingGroupId, autoClearDepthStencil) {
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
+            this._renderingManager._useSceneAutoClearSetup = false;
         };
         RenderTargetTexture.prototype.clone = function () {
             var textureSize = this.getSize();
@@ -67162,7 +67169,7 @@ var BABYLON;
             }
             this.onActivateObservable.notifyObservers(camera);
             // 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);
             }
             if (this._reusable) {
@@ -89282,6 +89289,7 @@ var BABYLON;
             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
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
+            this.utilityLayerScene._allowPostProcessClear = false;
             originalScene.getEngine().scenes.pop();
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
@@ -89399,7 +89407,27 @@ var BABYLON;
          */
         UtilityLayerRenderer.prototype.render = function () {
             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
@@ -89495,6 +89523,10 @@ var BABYLON;
              */
             this.enabled = true;
             /**
+             * If camera controls should be detached during the drag
+             */
+            this.detachCameraControls = true;
+            /**
              * If set, the drag plane/axis will be rotated based on the attached mesh's world rotation (Default: true)
              */
             this.useObjectOrienationForDragging = true;
@@ -89552,6 +89584,10 @@ var BABYLON;
                     PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
                     PointerDragBehavior._planeScene.detachControl();
                     this._scene.getEngine().scenes.pop();
+                    this._scene.onDisposeObservable.addOnce(function () {
+                        PointerDragBehavior._planeScene.dispose();
+                        PointerDragBehavior._planeScene = null;
+                    });
                 }
             }
             this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
@@ -89563,6 +89599,7 @@ var BABYLON;
             var pickPredicate = function (m) {
                 return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
             };
+            var attachedElement = null;
             this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
                 if (!_this.enabled) {
                     return;
@@ -89577,12 +89614,26 @@ var BABYLON;
                             _this.lastDragPosition.copyFrom(pickedPoint);
                             _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint, pointerId: _this.currentDraggingPointerID });
                             targetPosition.copyFrom(_this._attachedNode.absolutePosition);
+                            // Detatch camera controls
+                            if (_this.detachCameraControls && _this._scene.activeCamera) {
+                                if (_this._scene.activeCamera.inputs.attachedElement) {
+                                    attachedElement = _this._scene.activeCamera.inputs.attachedElement;
+                                    _this._scene.activeCamera.detachControl(_this._scene.activeCamera.inputs.attachedElement);
+                                }
+                                else {
+                                    attachedElement = null;
+                                }
+                            }
                         }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
                     if (_this.currentDraggingPointerID == pointerInfo.event.pointerId) {
                         _this.releaseDrag();
+                        // Reattach camera controls
+                        if (_this.detachCameraControls && attachedElement && _this._scene.activeCamera) {
+                            _this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -89848,6 +89899,10 @@ var BABYLON;
              * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
              */
             this.currentDraggingPointerID = -1;
+            /**
+             * If camera controls should be detached during the drag
+             */
+            this.detachCameraControls = true;
         }
         Object.defineProperty(SixDofDragBehavior.prototype, "name", {
             /**
@@ -89885,6 +89940,7 @@ var BABYLON;
             var pickPredicate = function (m) {
                 return _this._ownerNode == m || m.isDescendantOf(_this._ownerNode);
             };
+            var attachedElement = null;
             this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!_this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
@@ -89911,6 +89967,16 @@ var BABYLON;
                         _this._targetPosition.copyFrom(_this._virtualDragMesh.absolutePosition);
                         _this.dragging = true;
                         _this.currentDraggingPointerID = pointerInfo.event.pointerId;
+                        // Detatch camera controls
+                        if (_this.detachCameraControls && _this._scene.activeCamera) {
+                            if (_this._scene.activeCamera.inputs.attachedElement) {
+                                attachedElement = _this._scene.activeCamera.inputs.attachedElement;
+                                _this._scene.activeCamera.detachControl(_this._scene.activeCamera.inputs.attachedElement);
+                            }
+                            else {
+                                attachedElement = null;
+                            }
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
@@ -89920,6 +89986,10 @@ var BABYLON;
                         _this.currentDraggingPointerID = -1;
                         pickedMesh = null;
                         _this._virtualOriginMesh.removeChild(_this._virtualDragMesh);
+                        // Reattach camera controls
+                        if (_this.detachCameraControls && attachedElement && _this._scene.activeCamera) {
+                            _this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -90005,6 +90075,178 @@ var BABYLON;
 var 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.
      */
     var Gizmo = /** @class */ (function () {
@@ -104206,11 +104448,15 @@ var BABYLON;
             else {
                 _this._useDirectMapping = options.useDirectMapping;
             }
+            _this._setReady(false);
             // create
             var tempOptions = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
             var material = _this._material = new BABYLON.BackgroundMaterial(name + "_material", scene);
             var texture = _this._videoTexture = new BABYLON.VideoTexture(name + "_texture", urlsOrVideo, scene, false, _this._useDirectMapping, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, tempOptions);
             _this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
+            texture.onLoadObservable.addOnce(function () {
+                _this._setReady(true);
+            });
             // configure material
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;
@@ -104309,6 +104555,7 @@ var BABYLON;
             else {
                 _this._useDirectMapping = options.useDirectMapping;
             }
+            _this._setReady(false);
             // create
             var material = _this._material = new BABYLON.BackgroundMaterial(name + "_material", scene);
             _this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
@@ -104317,6 +104564,9 @@ var BABYLON;
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;
             _this.photoTexture = new BABYLON.Texture(urlOfPhoto, scene, true, !_this._useDirectMapping);
+            _this.photoTexture.onLoadObservable.addOnce(function () {
+                _this._setReady(true);
+            });
             // configure mesh
             _this._mesh.material = material;
             _this._mesh.parent = _this;

+ 319 - 69
dist/preview release/babylon.no-module.max.js

@@ -9437,6 +9437,10 @@ var BABYLON;
          * @param fileName defines the name of the downloaded file
          */
         Tools.Download = function (blob, fileName) {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             document.body.appendChild(a);
@@ -11660,7 +11664,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "3.3.0-alpha.11";
+                return "3.3.0-alpha.12";
             },
             enumerable: true,
             configurable: true
@@ -22805,12 +22809,16 @@ var BABYLON;
 (function (BABYLON) {
     var RenderingManager = /** @class */ (function () {
         function RenderingManager(scene) {
+            /**
+             * Hidden
+             */
+            this._useSceneAutoClearSetup = false;
             this._renderingGroups = new Array();
             this._autoClearDepthStencil = {};
             this._customOpaqueSortCompareFn = {};
             this._customAlphaTestSortCompareFn = {};
             this._customTransparentSortCompareFn = {};
-            this._renderinGroupInfo = null;
+            this._renderingGroupInfo = new BABYLON.RenderingGroupInfo();
             this._scene = scene;
             for (var i = RenderingManager.MIN_RENDERINGGROUPS; i < RenderingManager.MAX_RENDERINGGROUPS; i++) {
                 this._autoClearDepthStencil[i] = { autoClear: true, depth: true, stencil: true };
@@ -22826,17 +22834,10 @@ var BABYLON;
             this._depthStencilBufferAlreadyCleaned = true;
         };
         RenderingManager.prototype.render = function (customRenderFunction, activeMeshes, renderParticles, renderSprites) {
-            // Check if there's at least on observer on the onRenderingGroupObservable and initialize things to fire it
-            var observable = this._scene.onRenderingGroupObservable.hasObservers() ? this._scene.onRenderingGroupObservable : null;
-            var info = null;
-            if (observable) {
-                if (!this._renderinGroupInfo) {
-                    this._renderinGroupInfo = new BABYLON.RenderingGroupInfo();
-                }
-                info = this._renderinGroupInfo;
-                info.scene = this._scene;
-                info.camera = this._scene.activeCamera;
-            }
+            // Update the observable context (not null as it only goes away on dispose)
+            var info = this._renderingGroupInfo;
+            info.scene = this._scene;
+            info.camera = this._scene.activeCamera;
             // Dispatch sprites
             if (renderSprites) {
                 for (var index = 0; index < this._scene.spriteManagers.length; index++) {
@@ -22848,47 +22849,33 @@ var BABYLON;
             for (var index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
                 this._depthStencilBufferAlreadyCleaned = index === RenderingManager.MIN_RENDERINGGROUPS;
                 var renderingGroup = this._renderingGroups[index];
-                if (!renderingGroup && !observable)
+                if (!renderingGroup)
                     continue;
-                var renderingGroupMask = 0;
-                // Fire PRECLEAR stage
-                if (observable && info) {
-                    renderingGroupMask = Math.pow(2, index);
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRECLEAR;
-                    info.renderingGroupId = index;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
+                var renderingGroupMask = Math.pow(2, index);
+                info.renderingGroupId = index;
+                // Before Observable
+                this._scene.onBeforeRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
                 // Clear depth/stencil if needed
                 if (RenderingManager.AUTOCLEAR) {
-                    var autoClear = this._autoClearDepthStencil[index];
+                    var autoClear = this._useSceneAutoClearSetup ?
+                        this._scene.getAutoClearDepthStencilSetup(index) :
+                        this._autoClearDepthStencil[index];
                     if (autoClear && autoClear.autoClear) {
                         this._clearDepthStencilBuffer(autoClear.depth, autoClear.stencil);
                     }
                 }
-                if (observable && info) {
-                    // Fire PREOPAQUE stage
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PREOPAQUE;
-                    observable.notifyObservers(info, renderingGroupMask);
-                    // Fire PRETRANSPARENT stage
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRETRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
-                if (renderingGroup) {
-                    for (var _i = 0, _a = this._scene._beforeRenderingGroupDrawStage; _i < _a.length; _i++) {
-                        var step = _a[_i];
-                        step.action(index);
-                    }
-                    renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
-                    for (var _b = 0, _c = this._scene._afterRenderingGroupDrawStage; _b < _c.length; _b++) {
-                        var step = _c[_b];
-                        step.action(index);
-                    }
+                // Render
+                for (var _i = 0, _a = this._scene._beforeRenderingGroupDrawStage; _i < _a.length; _i++) {
+                    var step = _a[_i];
+                    step.action(index);
                 }
-                // Fire POSTTRANSPARENT stage
-                if (observable && info) {
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_POSTTRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
+                renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
+                for (var _b = 0, _c = this._scene._afterRenderingGroupDrawStage; _b < _c.length; _b++) {
+                    var step = _c[_b];
+                    step.action(index);
                 }
+                // After Observable
+                this._scene.onAfterRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
             }
         };
         RenderingManager.prototype.reset = function () {
@@ -22902,6 +22889,7 @@ var BABYLON;
         RenderingManager.prototype.dispose = function () {
             this.freeRenderingGroups();
             this._renderingGroups.length = 0;
+            this._renderingGroupInfo = null;
         };
         /**
          * Clear the info related to rendering groups preventing retention points during dispose.
@@ -22983,6 +22971,15 @@ var BABYLON;
             };
         };
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        RenderingManager.prototype.getAutoClearDepthStencilSetup = function (index) {
+            return this._autoClearDepthStencil[index];
+        };
+        /**
          * The max id used for rendering groups (not included)
          */
         RenderingManager.MAX_RENDERINGGROUPS = 4;
@@ -23641,26 +23638,6 @@ var BABYLON;
     var RenderingGroupInfo = /** @class */ (function () {
         function RenderingGroupInfo() {
         }
-        /**
-         * Stage corresponding to the very first hook in the renderingGroup phase: before the render buffer may be cleared
-         * This stage will be fired no matter what
-         */
-        RenderingGroupInfo.STAGE_PRECLEAR = 1;
-        /**
-         * Called before opaque object are rendered.
-         * This stage will be fired only if there's 3D Opaque content to render
-         */
-        RenderingGroupInfo.STAGE_PREOPAQUE = 2;
-        /**
-         * Called after the opaque objects are rendered and before the transparent ones
-         * This stage will be fired only if there's 3D transparent content to render
-         */
-        RenderingGroupInfo.STAGE_PRETRANSPARENT = 3;
-        /**
-         * Called after the transparent object are rendered, last hook of the renderingGroup phase
-         * This stage will be fired no matter what
-         */
-        RenderingGroupInfo.STAGE_POSTTRANSPARENT = 4;
         return RenderingGroupInfo;
     }());
     BABYLON.RenderingGroupInfo = RenderingGroupInfo;
@@ -23876,11 +23853,17 @@ var BABYLON;
             */
             _this.onAfterStepObservable = new BABYLON.Observable();
             /**
-             * This Observable will be triggered for each stage of each renderingGroup of each rendered camera.
+             * This Observable will be triggered before rendering each renderingGroup of each rendered camera.
              * The RenderinGroupInfo class contains all the information about the context in which the observable is called
              * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
              */
-            _this.onRenderingGroupObservable = new BABYLON.Observable();
+            _this.onBeforeRenderingGroupObservable = new BABYLON.Observable();
+            /**
+             * This Observable will be triggered after rendering each renderingGroup of each rendered camera.
+             * The RenderinGroupInfo class contains all the information about the context in which the observable is called
+             * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
+             */
+            _this.onAfterRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             _this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
@@ -24142,6 +24125,8 @@ var BABYLON;
              */
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._activeMeshesFrozen = false;
+            /** @hidden */
+            _this._allowPostProcessClear = true;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine.scenes.push(_this);
@@ -28002,6 +27987,8 @@ var BABYLON;
             this.onBeforeAnimationsObservable.clear();
             this.onAfterAnimationsObservable.clear();
             this.onDataLoadedObservable.clear();
+            this.onBeforeRenderingGroupObservable.clear();
+            this.onAfterRenderingGroupObservable.clear();
             this.detachControl();
             // Release sounds & sounds tracks
             if (BABYLON.AudioEngine) {
@@ -28816,6 +28803,15 @@ var BABYLON;
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil, depth, stencil);
         };
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        Scene.prototype.getAutoClearDepthStencilSetup = function (index) {
+            return this._renderingManager.getAutoClearDepthStencilSetup(index);
+        };
+        /**
          * Will flag all materials as dirty to trigger new shader compilation
          * @param flag defines the flag used to specify which material part must be marked as dirty
          * @param predicate If not null, it will be used to specifiy if a material has to be marked as dirty
@@ -58153,9 +58149,15 @@ var BABYLON;
             return "LinesMesh";
         };
         Object.defineProperty(LinesMesh.prototype, "material", {
+            /**
+             * @hidden
+             */
             get: function () {
                 return this._colorShader;
             },
+            /**
+             * @hidden
+             */
             set: function (value) {
                 // Do nothing
             },
@@ -58163,6 +58165,9 @@ var BABYLON;
             configurable: true
         });
         Object.defineProperty(LinesMesh.prototype, "checkCollisions", {
+            /**
+             * @hidden
+             */
             get: function () {
                 return false;
             },
@@ -64942,6 +64947,7 @@ var BABYLON;
             _this._doNotChangeAspectRatio = doNotChangeAspectRatio;
             // Rendering groups
             _this._renderingManager = new BABYLON.RenderingManager(scene);
+            _this._renderingManager._useSceneAutoClearSetup = true;
             if (isMulti) {
                 return _this;
             }
@@ -65442,6 +65448,7 @@ var BABYLON;
          */
         RenderTargetTexture.prototype.setRenderingAutoClearDepthStencil = function (renderingGroupId, autoClearDepthStencil) {
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
+            this._renderingManager._useSceneAutoClearSetup = false;
         };
         RenderTargetTexture.prototype.clone = function () {
             var textureSize = this.getSize();
@@ -67129,7 +67136,7 @@ var BABYLON;
             }
             this.onActivateObservable.notifyObservers(camera);
             // 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);
             }
             if (this._reusable) {
@@ -89249,6 +89256,7 @@ var BABYLON;
             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
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
+            this.utilityLayerScene._allowPostProcessClear = false;
             originalScene.getEngine().scenes.pop();
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
@@ -89366,7 +89374,27 @@ var BABYLON;
          */
         UtilityLayerRenderer.prototype.render = function () {
             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
@@ -89462,6 +89490,10 @@ var BABYLON;
              */
             this.enabled = true;
             /**
+             * If camera controls should be detached during the drag
+             */
+            this.detachCameraControls = true;
+            /**
              * If set, the drag plane/axis will be rotated based on the attached mesh's world rotation (Default: true)
              */
             this.useObjectOrienationForDragging = true;
@@ -89519,6 +89551,10 @@ var BABYLON;
                     PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
                     PointerDragBehavior._planeScene.detachControl();
                     this._scene.getEngine().scenes.pop();
+                    this._scene.onDisposeObservable.addOnce(function () {
+                        PointerDragBehavior._planeScene.dispose();
+                        PointerDragBehavior._planeScene = null;
+                    });
                 }
             }
             this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
@@ -89530,6 +89566,7 @@ var BABYLON;
             var pickPredicate = function (m) {
                 return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
             };
+            var attachedElement = null;
             this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
                 if (!_this.enabled) {
                     return;
@@ -89544,12 +89581,26 @@ var BABYLON;
                             _this.lastDragPosition.copyFrom(pickedPoint);
                             _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint, pointerId: _this.currentDraggingPointerID });
                             targetPosition.copyFrom(_this._attachedNode.absolutePosition);
+                            // Detatch camera controls
+                            if (_this.detachCameraControls && _this._scene.activeCamera) {
+                                if (_this._scene.activeCamera.inputs.attachedElement) {
+                                    attachedElement = _this._scene.activeCamera.inputs.attachedElement;
+                                    _this._scene.activeCamera.detachControl(_this._scene.activeCamera.inputs.attachedElement);
+                                }
+                                else {
+                                    attachedElement = null;
+                                }
+                            }
                         }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
                     if (_this.currentDraggingPointerID == pointerInfo.event.pointerId) {
                         _this.releaseDrag();
+                        // Reattach camera controls
+                        if (_this.detachCameraControls && attachedElement && _this._scene.activeCamera) {
+                            _this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -89815,6 +89866,10 @@ var BABYLON;
              * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
              */
             this.currentDraggingPointerID = -1;
+            /**
+             * If camera controls should be detached during the drag
+             */
+            this.detachCameraControls = true;
         }
         Object.defineProperty(SixDofDragBehavior.prototype, "name", {
             /**
@@ -89852,6 +89907,7 @@ var BABYLON;
             var pickPredicate = function (m) {
                 return _this._ownerNode == m || m.isDescendantOf(_this._ownerNode);
             };
+            var attachedElement = null;
             this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!_this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
@@ -89878,6 +89934,16 @@ var BABYLON;
                         _this._targetPosition.copyFrom(_this._virtualDragMesh.absolutePosition);
                         _this.dragging = true;
                         _this.currentDraggingPointerID = pointerInfo.event.pointerId;
+                        // Detatch camera controls
+                        if (_this.detachCameraControls && _this._scene.activeCamera) {
+                            if (_this._scene.activeCamera.inputs.attachedElement) {
+                                attachedElement = _this._scene.activeCamera.inputs.attachedElement;
+                                _this._scene.activeCamera.detachControl(_this._scene.activeCamera.inputs.attachedElement);
+                            }
+                            else {
+                                attachedElement = null;
+                            }
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
@@ -89887,6 +89953,10 @@ var BABYLON;
                         _this.currentDraggingPointerID = -1;
                         pickedMesh = null;
                         _this._virtualOriginMesh.removeChild(_this._virtualDragMesh);
+                        // Reattach camera controls
+                        if (_this.detachCameraControls && attachedElement && _this._scene.activeCamera) {
+                            _this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -89972,6 +90042,178 @@ var BABYLON;
 var 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.
      */
     var Gizmo = /** @class */ (function () {
@@ -104173,11 +104415,15 @@ var BABYLON;
             else {
                 _this._useDirectMapping = options.useDirectMapping;
             }
+            _this._setReady(false);
             // create
             var tempOptions = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
             var material = _this._material = new BABYLON.BackgroundMaterial(name + "_material", scene);
             var texture = _this._videoTexture = new BABYLON.VideoTexture(name + "_texture", urlsOrVideo, scene, false, _this._useDirectMapping, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, tempOptions);
             _this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
+            texture.onLoadObservable.addOnce(function () {
+                _this._setReady(true);
+            });
             // configure material
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;
@@ -104276,6 +104522,7 @@ var BABYLON;
             else {
                 _this._useDirectMapping = options.useDirectMapping;
             }
+            _this._setReady(false);
             // create
             var material = _this._material = new BABYLON.BackgroundMaterial(name + "_material", scene);
             _this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
@@ -104284,6 +104531,9 @@ var BABYLON;
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;
             _this.photoTexture = new BABYLON.Texture(urlOfPhoto, scene, true, !_this._useDirectMapping);
+            _this.photoTexture.onLoadObservable.addOnce(function () {
+                _this._setReady(true);
+            });
             // configure mesh
             _this._mesh.material = material;
             _this._mesh.parent = _this;

Файловите разлики са ограничени, защото са твърде много
+ 26 - 26
dist/preview release/babylon.worker.js


Файловите разлики са ограничени, защото са твърде много
+ 321 - 71
dist/preview release/es6.js


+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 6 - 1
dist/preview release/gui/babylon.gui.d.ts

@@ -2344,6 +2344,11 @@ declare module BABYLON.GUI {
          */
         constructor(name?: string);
         /**
+         * Force the container to update the layout. Please note that it will not take blockLayout property in account
+         * @returns the current container
+         */
+        updateLayout(): Container3D;
+        /**
          * Gets a boolean indicating if the given control is in the children of this control
          * @param control defines the control to check
          * @returns true if the control is in the child list
@@ -2589,7 +2594,7 @@ declare module BABYLON.GUI {
          */
         rows: int;
         /**
-         * Creates new SpherePanel
+         * Creates new VolumeBasedPanel
          */
         constructor();
         protected _arrangeChildren(): void;

+ 16 - 4
dist/preview release/gui/babylon.gui.js

@@ -7988,6 +7988,14 @@ var BABYLON;
                 configurable: true
             });
             /**
+             * Force the container to update the layout. Please note that it will not take blockLayout property in account
+             * @returns the current container
+             */
+            Container3D.prototype.updateLayout = function () {
+                this._arrangeChildren();
+                return this;
+            };
+            /**
              * Gets a boolean indicating if the given control is in the children of this control
              * @param control defines the control to check
              * @returns true if the control is in the child list
@@ -8691,7 +8699,7 @@ var BABYLON;
         var VolumeBasedPanel = /** @class */ (function (_super) {
             __extends(VolumeBasedPanel, _super);
             /**
-             * Creates new SpherePanel
+             * Creates new VolumeBasedPanel
              */
             function VolumeBasedPanel() {
                 var _this = _super.call(this) || this;
@@ -8791,9 +8799,13 @@ var BABYLON;
                     }
                     controlCount++;
                     child.mesh.computeWorldMatrix(true);
-                    child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, BABYLON.Tmp.Matrix[0]);
-                    var boundingBox = child.mesh.getBoundingInfo().boundingBox;
-                    var extendSize = BABYLON.Vector3.TransformNormal(boundingBox.extendSize, BABYLON.Tmp.Matrix[0]);
+                    //   child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, Tmp.Matrix[0]);
+                    var boundingBox = child.mesh.getHierarchyBoundingVectors();
+                    var extendSize = BABYLON.Tmp.Vector3[0];
+                    var diff = BABYLON.Tmp.Vector3[1];
+                    boundingBox.max.subtractToRef(boundingBox.min, diff);
+                    diff.scaleInPlace(0.5);
+                    BABYLON.Vector3.TransformNormalToRef(diff, currentInverseWorld, extendSize);
                     this._cellWidth = Math.max(this._cellWidth, extendSize.x * 2);
                     this._cellHeight = Math.max(this._cellHeight, extendSize.y * 2);
                 }

Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


+ 6 - 1
dist/preview release/gui/babylon.gui.module.d.ts

@@ -2349,6 +2349,11 @@ declare module BABYLON.GUI {
          */
         constructor(name?: string);
         /**
+         * Force the container to update the layout. Please note that it will not take blockLayout property in account
+         * @returns the current container
+         */
+        updateLayout(): Container3D;
+        /**
          * Gets a boolean indicating if the given control is in the children of this control
          * @param control defines the control to check
          * @returns true if the control is in the child list
@@ -2594,7 +2599,7 @@ declare module BABYLON.GUI {
          */
         rows: int;
         /**
-         * Creates new SpherePanel
+         * Creates new VolumeBasedPanel
          */
         constructor();
         protected _arrangeChildren(): void;

+ 1 - 1
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 2 - 2
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.3.0-alpha.11"
+        "babylonjs-gltf2interface": "3.3.0-alpha.12"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

+ 1 - 1
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 6 - 4
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -3276,10 +3276,12 @@ var BABYLON;
                 quaternion[1] *= -1;
             };
             _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
-                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
-                tangent.x /= length;
-                tangent.y /= length;
-                tangent.z /= length;
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z * tangent.z);
+                if (length > 0) {
+                    tangent.x /= length;
+                    tangent.y /= length;
+                    tangent.z /= length;
+                }
             };
             return _GLTFUtilities;
         }());

Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 6 - 4
dist/preview release/serializers/babylonjs.serializers.js

@@ -3437,10 +3437,12 @@ var BABYLON;
                 quaternion[1] *= -1;
             };
             _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
-                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
-                tangent.x /= length;
-                tangent.y /= length;
-                tangent.z /= length;
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z * tangent.z);
+                if (length > 0) {
+                    tangent.x /= length;
+                    tangent.y /= length;
+                    tangent.z /= length;
+                }
             };
             return _GLTFUtilities;
         }());

Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 2 - 2
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.3.0-alpha.11"
+        "babylonjs-gltf2interface": "3.3.0-alpha.12"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

Файловите разлики са ограничени, защото са твърде много
+ 34 - 34
dist/preview release/viewer/babylon.viewer.js


+ 311 - 69
dist/preview release/viewer/babylon.viewer.max.js

@@ -9558,6 +9558,10 @@ var BABYLON;
          * @param fileName defines the name of the downloaded file
          */
         Tools.Download = function (blob, fileName) {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             document.body.appendChild(a);
@@ -11781,7 +11785,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "3.3.0-alpha.11";
+                return "3.3.0-alpha.12";
             },
             enumerable: true,
             configurable: true
@@ -22926,12 +22930,16 @@ var BABYLON;
 (function (BABYLON) {
     var RenderingManager = /** @class */ (function () {
         function RenderingManager(scene) {
+            /**
+             * Hidden
+             */
+            this._useSceneAutoClearSetup = false;
             this._renderingGroups = new Array();
             this._autoClearDepthStencil = {};
             this._customOpaqueSortCompareFn = {};
             this._customAlphaTestSortCompareFn = {};
             this._customTransparentSortCompareFn = {};
-            this._renderinGroupInfo = null;
+            this._renderingGroupInfo = new BABYLON.RenderingGroupInfo();
             this._scene = scene;
             for (var i = RenderingManager.MIN_RENDERINGGROUPS; i < RenderingManager.MAX_RENDERINGGROUPS; i++) {
                 this._autoClearDepthStencil[i] = { autoClear: true, depth: true, stencil: true };
@@ -22947,17 +22955,10 @@ var BABYLON;
             this._depthStencilBufferAlreadyCleaned = true;
         };
         RenderingManager.prototype.render = function (customRenderFunction, activeMeshes, renderParticles, renderSprites) {
-            // Check if there's at least on observer on the onRenderingGroupObservable and initialize things to fire it
-            var observable = this._scene.onRenderingGroupObservable.hasObservers() ? this._scene.onRenderingGroupObservable : null;
-            var info = null;
-            if (observable) {
-                if (!this._renderinGroupInfo) {
-                    this._renderinGroupInfo = new BABYLON.RenderingGroupInfo();
-                }
-                info = this._renderinGroupInfo;
-                info.scene = this._scene;
-                info.camera = this._scene.activeCamera;
-            }
+            // Update the observable context (not null as it only goes away on dispose)
+            var info = this._renderingGroupInfo;
+            info.scene = this._scene;
+            info.camera = this._scene.activeCamera;
             // Dispatch sprites
             if (renderSprites) {
                 for (var index = 0; index < this._scene.spriteManagers.length; index++) {
@@ -22969,47 +22970,33 @@ var BABYLON;
             for (var index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
                 this._depthStencilBufferAlreadyCleaned = index === RenderingManager.MIN_RENDERINGGROUPS;
                 var renderingGroup = this._renderingGroups[index];
-                if (!renderingGroup && !observable)
+                if (!renderingGroup)
                     continue;
-                var renderingGroupMask = 0;
-                // Fire PRECLEAR stage
-                if (observable && info) {
-                    renderingGroupMask = Math.pow(2, index);
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRECLEAR;
-                    info.renderingGroupId = index;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
+                var renderingGroupMask = Math.pow(2, index);
+                info.renderingGroupId = index;
+                // Before Observable
+                this._scene.onBeforeRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
                 // Clear depth/stencil if needed
                 if (RenderingManager.AUTOCLEAR) {
-                    var autoClear = this._autoClearDepthStencil[index];
+                    var autoClear = this._useSceneAutoClearSetup ?
+                        this._scene.getAutoClearDepthStencilSetup(index) :
+                        this._autoClearDepthStencil[index];
                     if (autoClear && autoClear.autoClear) {
                         this._clearDepthStencilBuffer(autoClear.depth, autoClear.stencil);
                     }
                 }
-                if (observable && info) {
-                    // Fire PREOPAQUE stage
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PREOPAQUE;
-                    observable.notifyObservers(info, renderingGroupMask);
-                    // Fire PRETRANSPARENT stage
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRETRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
-                if (renderingGroup) {
-                    for (var _i = 0, _a = this._scene._beforeRenderingGroupDrawStage; _i < _a.length; _i++) {
-                        var step = _a[_i];
-                        step.action(index);
-                    }
-                    renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
-                    for (var _b = 0, _c = this._scene._afterRenderingGroupDrawStage; _b < _c.length; _b++) {
-                        var step = _c[_b];
-                        step.action(index);
-                    }
+                // Render
+                for (var _i = 0, _a = this._scene._beforeRenderingGroupDrawStage; _i < _a.length; _i++) {
+                    var step = _a[_i];
+                    step.action(index);
                 }
-                // Fire POSTTRANSPARENT stage
-                if (observable && info) {
-                    info.renderStage = BABYLON.RenderingGroupInfo.STAGE_POSTTRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
+                renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
+                for (var _b = 0, _c = this._scene._afterRenderingGroupDrawStage; _b < _c.length; _b++) {
+                    var step = _c[_b];
+                    step.action(index);
                 }
+                // After Observable
+                this._scene.onAfterRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
             }
         };
         RenderingManager.prototype.reset = function () {
@@ -23023,6 +23010,7 @@ var BABYLON;
         RenderingManager.prototype.dispose = function () {
             this.freeRenderingGroups();
             this._renderingGroups.length = 0;
+            this._renderingGroupInfo = null;
         };
         /**
          * Clear the info related to rendering groups preventing retention points during dispose.
@@ -23104,6 +23092,15 @@ var BABYLON;
             };
         };
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        RenderingManager.prototype.getAutoClearDepthStencilSetup = function (index) {
+            return this._autoClearDepthStencil[index];
+        };
+        /**
          * The max id used for rendering groups (not included)
          */
         RenderingManager.MAX_RENDERINGGROUPS = 4;
@@ -23762,26 +23759,6 @@ var BABYLON;
     var RenderingGroupInfo = /** @class */ (function () {
         function RenderingGroupInfo() {
         }
-        /**
-         * Stage corresponding to the very first hook in the renderingGroup phase: before the render buffer may be cleared
-         * This stage will be fired no matter what
-         */
-        RenderingGroupInfo.STAGE_PRECLEAR = 1;
-        /**
-         * Called before opaque object are rendered.
-         * This stage will be fired only if there's 3D Opaque content to render
-         */
-        RenderingGroupInfo.STAGE_PREOPAQUE = 2;
-        /**
-         * Called after the opaque objects are rendered and before the transparent ones
-         * This stage will be fired only if there's 3D transparent content to render
-         */
-        RenderingGroupInfo.STAGE_PRETRANSPARENT = 3;
-        /**
-         * Called after the transparent object are rendered, last hook of the renderingGroup phase
-         * This stage will be fired no matter what
-         */
-        RenderingGroupInfo.STAGE_POSTTRANSPARENT = 4;
         return RenderingGroupInfo;
     }());
     BABYLON.RenderingGroupInfo = RenderingGroupInfo;
@@ -23997,11 +23974,17 @@ var BABYLON;
             */
             _this.onAfterStepObservable = new BABYLON.Observable();
             /**
-             * This Observable will be triggered for each stage of each renderingGroup of each rendered camera.
+             * This Observable will be triggered before rendering each renderingGroup of each rendered camera.
+             * The RenderinGroupInfo class contains all the information about the context in which the observable is called
+             * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
+             */
+            _this.onBeforeRenderingGroupObservable = new BABYLON.Observable();
+            /**
+             * This Observable will be triggered after rendering each renderingGroup of each rendered camera.
              * The RenderinGroupInfo class contains all the information about the context in which the observable is called
              * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
              */
-            _this.onRenderingGroupObservable = new BABYLON.Observable();
+            _this.onAfterRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             _this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
@@ -24263,6 +24246,8 @@ var BABYLON;
              */
             _this._afterCameraDrawStage = BABYLON.Stage.Create();
             _this._activeMeshesFrozen = false;
+            /** @hidden */
+            _this._allowPostProcessClear = true;
             _this._tempPickingRay = BABYLON.Ray ? BABYLON.Ray.Zero() : null;
             _this._engine = engine || BABYLON.Engine.LastCreatedEngine;
             _this._engine.scenes.push(_this);
@@ -28123,6 +28108,8 @@ var BABYLON;
             this.onBeforeAnimationsObservable.clear();
             this.onAfterAnimationsObservable.clear();
             this.onDataLoadedObservable.clear();
+            this.onBeforeRenderingGroupObservable.clear();
+            this.onAfterRenderingGroupObservable.clear();
             this.detachControl();
             // Release sounds & sounds tracks
             if (BABYLON.AudioEngine) {
@@ -28937,6 +28924,15 @@ var BABYLON;
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil, depth, stencil);
         };
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        Scene.prototype.getAutoClearDepthStencilSetup = function (index) {
+            return this._renderingManager.getAutoClearDepthStencilSetup(index);
+        };
+        /**
          * Will flag all materials as dirty to trigger new shader compilation
          * @param flag defines the flag used to specify which material part must be marked as dirty
          * @param predicate If not null, it will be used to specifiy if a material has to be marked as dirty
@@ -58274,9 +58270,15 @@ var BABYLON;
             return "LinesMesh";
         };
         Object.defineProperty(LinesMesh.prototype, "material", {
+            /**
+             * @hidden
+             */
             get: function () {
                 return this._colorShader;
             },
+            /**
+             * @hidden
+             */
             set: function (value) {
                 // Do nothing
             },
@@ -58284,6 +58286,9 @@ var BABYLON;
             configurable: true
         });
         Object.defineProperty(LinesMesh.prototype, "checkCollisions", {
+            /**
+             * @hidden
+             */
             get: function () {
                 return false;
             },
@@ -65063,6 +65068,7 @@ var BABYLON;
             _this._doNotChangeAspectRatio = doNotChangeAspectRatio;
             // Rendering groups
             _this._renderingManager = new BABYLON.RenderingManager(scene);
+            _this._renderingManager._useSceneAutoClearSetup = true;
             if (isMulti) {
                 return _this;
             }
@@ -65563,6 +65569,7 @@ var BABYLON;
          */
         RenderTargetTexture.prototype.setRenderingAutoClearDepthStencil = function (renderingGroupId, autoClearDepthStencil) {
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
+            this._renderingManager._useSceneAutoClearSetup = false;
         };
         RenderTargetTexture.prototype.clone = function () {
             var textureSize = this.getSize();
@@ -67250,7 +67257,7 @@ var BABYLON;
             }
             this.onActivateObservable.notifyObservers(camera);
             // 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);
             }
             if (this._reusable) {
@@ -89370,6 +89377,7 @@ var BABYLON;
             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
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
+            this.utilityLayerScene._allowPostProcessClear = false;
             originalScene.getEngine().scenes.pop();
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
             this.utilityLayerScene.detachControl();
@@ -89487,7 +89495,27 @@ var BABYLON;
          */
         UtilityLayerRenderer.prototype.render = function () {
             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
@@ -89583,6 +89611,10 @@ var BABYLON;
              */
             this.enabled = true;
             /**
+             * If camera controls should be detached during the drag
+             */
+            this.detachCameraControls = true;
+            /**
              * If set, the drag plane/axis will be rotated based on the attached mesh's world rotation (Default: true)
              */
             this.useObjectOrienationForDragging = true;
@@ -89640,6 +89672,10 @@ var BABYLON;
                     PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
                     PointerDragBehavior._planeScene.detachControl();
                     this._scene.getEngine().scenes.pop();
+                    this._scene.onDisposeObservable.addOnce(function () {
+                        PointerDragBehavior._planeScene.dispose();
+                        PointerDragBehavior._planeScene = null;
+                    });
                 }
             }
             this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
@@ -89651,6 +89687,7 @@ var BABYLON;
             var pickPredicate = function (m) {
                 return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
             };
+            var attachedElement = null;
             this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
                 if (!_this.enabled) {
                     return;
@@ -89665,12 +89702,26 @@ var BABYLON;
                             _this.lastDragPosition.copyFrom(pickedPoint);
                             _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint, pointerId: _this.currentDraggingPointerID });
                             targetPosition.copyFrom(_this._attachedNode.absolutePosition);
+                            // Detatch camera controls
+                            if (_this.detachCameraControls && _this._scene.activeCamera) {
+                                if (_this._scene.activeCamera.inputs.attachedElement) {
+                                    attachedElement = _this._scene.activeCamera.inputs.attachedElement;
+                                    _this._scene.activeCamera.detachControl(_this._scene.activeCamera.inputs.attachedElement);
+                                }
+                                else {
+                                    attachedElement = null;
+                                }
+                            }
                         }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
                     if (_this.currentDraggingPointerID == pointerInfo.event.pointerId) {
                         _this.releaseDrag();
+                        // Reattach camera controls
+                        if (_this.detachCameraControls && attachedElement && _this._scene.activeCamera) {
+                            _this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -89936,6 +89987,10 @@ var BABYLON;
              * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
              */
             this.currentDraggingPointerID = -1;
+            /**
+             * If camera controls should be detached during the drag
+             */
+            this.detachCameraControls = true;
         }
         Object.defineProperty(SixDofDragBehavior.prototype, "name", {
             /**
@@ -89973,6 +90028,7 @@ var BABYLON;
             var pickPredicate = function (m) {
                 return _this._ownerNode == m || m.isDescendantOf(_this._ownerNode);
             };
+            var attachedElement = null;
             this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!_this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
@@ -89999,6 +90055,16 @@ var BABYLON;
                         _this._targetPosition.copyFrom(_this._virtualDragMesh.absolutePosition);
                         _this.dragging = true;
                         _this.currentDraggingPointerID = pointerInfo.event.pointerId;
+                        // Detatch camera controls
+                        if (_this.detachCameraControls && _this._scene.activeCamera) {
+                            if (_this._scene.activeCamera.inputs.attachedElement) {
+                                attachedElement = _this._scene.activeCamera.inputs.attachedElement;
+                                _this._scene.activeCamera.detachControl(_this._scene.activeCamera.inputs.attachedElement);
+                            }
+                            else {
+                                attachedElement = null;
+                            }
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
@@ -90008,6 +90074,10 @@ var BABYLON;
                         _this.currentDraggingPointerID = -1;
                         pickedMesh = null;
                         _this._virtualOriginMesh.removeChild(_this._virtualDragMesh);
+                        // Reattach camera controls
+                        if (_this.detachCameraControls && attachedElement && _this._scene.activeCamera) {
+                            _this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }
                 else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -90093,6 +90163,178 @@ var BABYLON;
 var 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.
      */
     var Gizmo = /** @class */ (function () {

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

@@ -7,7 +7,7 @@
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
-- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, scale, rotation and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
+- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, rotation, scale and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
 - New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh. [Doc](http://doc.babylonjs.com/how_to/meshbehavior) ([TrevorDev](https://github.com/TrevorDev))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
   - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
@@ -72,6 +72,7 @@
 - Added predicate function `targetMask` argument to `scene.beginWeightedAnimation`, `scene.beginAnimation`, `scene.stopAnimation`, and `animatable.stop` to allow for selective application of animations.  ([fmmoret](http://github.com/fmmoret))
 - Oculus GO and GearVR 3dof controllers will now rotate with the user's head if they turn around in their room ([TrevorDev](https://github.com/TrevorDev))
 - Added onPoseUpdatedFromDeviceObservable to webVRCamera to detect when the camera's pose has been updated ([TrevorDev](https://github.com/TrevorDev))
+- Added attachToBoxBehavior to attach UI to a bounding box ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo manager's internal gizmos are now public ([TrevorDev](https://github.com/TrevorDev))
 
 ### glTF Loader
@@ -147,7 +148,7 @@
 - Viewer is not using Engine.LastCreatedScene anymore, to support multiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
 - Template location was ignored if html was defined ([RaananW](https://github.com/RaananW))
 - Drag and Drop only worked if a model was already loaded before ([RaananW](https://github.com/RaananW))
-- It was not possible to add new custom optimizers, only to use existing ones ([RaananW](https://github.com/RaananW))
+- It was not possible to add new custom optimizers, only use existing ones ([RaananW](https://github.com/RaananW))
 
 ### Loaders
 
@@ -157,3 +158,4 @@
 ## Breaking changes
 
 - Fixing support for R and RG texture formats made us remove TextureFormat_R32F and TextureFormat_RG32F as they were mixing formats and types. Please, use the respective TextureFormat_R and TextureFormat_RG with the Float types ([sebavan](http://www.github.com/sebavan))
+- Replacing `scene.onRenderingGroupObservable` by `onBeforeRenderingGroupObservable` and `onAfterRenderingGroupObservable` to prevent the stage check ([sebavan](http://www.github.com/sebavan))

+ 10 - 1
gui/src/3D/controls/container3D.ts

@@ -48,6 +48,15 @@ export class Container3D extends Control3D {
     }
 
     /**
+     * Force the container to update the layout. Please note that it will not take blockLayout property in account
+     * @returns the current container
+     */
+    public updateLayout(): Container3D {
+        this._arrangeChildren();
+        return this;
+    }
+
+    /**
      * Gets a boolean indicating if the given control is in the children of this control
      * @param control defines the control to check
      * @returns true if the control is in the child list
@@ -148,4 +157,4 @@ export class Container3D extends Control3D {
     /** Control will rotate to look at negative z axis (0, 0, -1) */
     public static readonly FACEFORWARDREVERSED_ORIENTATION = 4;
 
-}
+}

+ 18 - 11
gui/src/3D/controls/volumeBasedPanel.ts

@@ -1,6 +1,6 @@
-import { Control3D } from "./control3D";
-import { Vector3, Tools, Matrix, Tmp } from "babylonjs";
 import { Container3D } from "./container3D";
+import { Tools, int, Matrix, Tmp, Vector3 } from "babylonjs";
+import { Control3D } from "./control3D";
 
 /**
  * Abstract class used to create a container panel deployed on the surface of a volume
@@ -21,7 +21,7 @@ export abstract class VolumeBasedPanel extends Container3D {
     public margin = 0;
 
     /**
-     * Gets or sets the orientation to apply to all controls (Container3D.FaceOriginReversedOrientation by default)
+     * Gets or sets the orientation to apply to all controls (BABYLON.Container3D.FaceOriginReversedOrientation by default)
     * | Value | Type                                | Description |
     * | ----- | ----------------------------------- | ----------- |
     * | 0     | UNSET_ORIENTATION                   |  Control rotation will remain unchanged |
@@ -50,11 +50,11 @@ export abstract class VolumeBasedPanel extends Container3D {
      * Gets or sets the number of columns requested (10 by default). 
      * The panel will automatically compute the number of rows based on number of child controls. 
      */
-    public get columns(): number {
+    public get columns(): int {
         return this._columns;
     }
 
-    public set columns(value: number) {
+    public set columns(value: int) {
         if (this._columns === value) {
             return;
         }
@@ -71,11 +71,11 @@ export abstract class VolumeBasedPanel extends Container3D {
      * Gets or sets a the number of rows requested. 
      * The panel will automatically compute the number of columns based on number of child controls. 
      */
-    public get rows(): number {
+    public get rows(): int {
         return this._rows;
     }
 
-    public set rows(value: number) {
+    public set rows(value: int) {
         if (this._rows === value) {
             return;
         }
@@ -89,7 +89,7 @@ export abstract class VolumeBasedPanel extends Container3D {
     }
 
     /**
-     * Creates new SpherePanel
+     * Creates new VolumeBasedPanel
      */
     public constructor() {
         super();
@@ -112,10 +112,17 @@ export abstract class VolumeBasedPanel extends Container3D {
 
             controlCount++;
             child.mesh.computeWorldMatrix(true);
-            child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, Tmp.Matrix[0]);
+            //   child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, Tmp.Matrix[0]);
+
+            let boundingBox = child.mesh.getHierarchyBoundingVectors();
+            let extendSize = Tmp.Vector3[0];
+            let diff = Tmp.Vector3[1];
+
+            boundingBox.max.subtractToRef(boundingBox.min, diff);
+
+            diff.scaleInPlace(0.5);
 
-            let boundingBox = child.mesh.getBoundingInfo().boundingBox;
-            let extendSize = Vector3.TransformNormal(boundingBox.extendSize, Tmp.Matrix[0]);
+            Vector3.TransformNormalToRef(diff, currentInverseWorld, extendSize);
 
             this._cellWidth = Math.max(this._cellWidth, extendSize.x * 2);
             this._cellHeight = Math.max(this._cellHeight, extendSize.y * 2);

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "3.3.0-alpha.11",
+    "version": "3.3.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 10 - 8
serializers/src/glTF/2.0/babylon.glTFUtilities.ts

@@ -170,8 +170,8 @@ module BABYLON.GLTF2 {
          * @param quaternion Source quaternion to convert to right-handed
          */
         public static _GetRightHandedQuaternionFromRef(quaternion: Quaternion) {
-             quaternion.x *= -1;
-             quaternion.y *= -1;
+            quaternion.x *= -1;
+            quaternion.y *= -1;
         }
 
         /**
@@ -179,15 +179,17 @@ module BABYLON.GLTF2 {
          * @param quaternion Source quaternion to convert to right-handed
          */
         public static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]) {
-             quaternion[0] *= -1;
-             quaternion[1] *= -1;
+            quaternion[0] *= -1;
+            quaternion[1] *= -1;
         }
 
         public static _NormalizeTangentFromRef(tangent: Vector4) {
-            const length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
-            tangent.x /= length;
-            tangent.y /= length;
-            tangent.z /= length;
+            const length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z * tangent.z);
+            if (length > 0) {
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            }
         }
     }
 }

+ 166 - 0
src/Behaviors/Mesh/babylon.attachToBoxBehavior.ts

@@ -0,0 +1,166 @@
+module BABYLON {
+    /**
+     * @hidden
+     */
+    class FaceDirectionInfo {
+        constructor(public direction:Vector3, public rotatedDirection = new BABYLON.Vector3(), public diff = 0, public ignore = false){}
+    }
+    
+    /**
+     * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+     */
+    export class AttachToBoxBehavior implements BABYLON.Behavior<BABYLON.Mesh> {
+        /**
+         *  The name of the behavior
+         */
+        public name = "AttachToBoxBehavior";
+        /**
+         * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
+         */
+        public distanceAwayFromFace = 0.15;
+        /**
+         * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
+         */
+        public distanceAwayFromBottomOfFace = 0.15;
+        private _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))];
+        private _target:Mesh;
+        private _scene:Scene;
+        private _onRenderObserver:Nullable<Observer<Scene>>;
+        private _tmpMatrix = new Matrix();
+        private _tmpVector = new Vector3();
+
+        /**
+         * 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
+         */
+        constructor(private ui: BABYLON.TransformNode){
+            /* Does nothing */
+        }
+
+        /**
+         *  Initializes the behavior
+         */
+        public init(){
+            /* Does nothing */
+        }
+        
+        private _closestFace(targetDirection:Vector3){
+            // Go over each face and calculate the angle between the face's normal and targetDirection
+            this._faceVectors.forEach((v)=>{
+                if(!this._target.rotationQuaternion){
+                    this._target.rotationQuaternion = 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((min,p)=>{
+                if(min.ignore){
+                    return p;
+                }else if(p.ignore){
+                    return min;
+                }else{
+                    return min.diff < p.diff ? min : p
+                }
+            }, this._faceVectors[0]);
+        }
+        
+        private _zeroVector = Vector3.Zero();
+        private _lookAtTmpMatrix = new Matrix();
+        private _lookAtToRef(pos:Vector3, up = new BABYLON.Vector3(0,1,0), ref:Quaternion){
+            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
+         */
+        attach(target: BABYLON.Mesh){
+            this._target = target;
+            this._scene = this._target.getScene();
+            
+            // Every frame, update the app bars position
+            this._onRenderObserver = this._scene.onBeforeRenderObservable.add(()=>{
+                if(!this._scene.activeCamera){
+                    return;
+                }
+
+                // Find the face closest to the cameras position
+                var cameraPos = this._scene.activeCamera.position;
+                if((<WebVRFreeCamera>this._scene.activeCamera).devicePosition){
+                    cameraPos = (<WebVRFreeCamera>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((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((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 = 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
+         */
+        detach(){
+            this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
+        }
+    }
+}

+ 24 - 0
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -65,6 +65,10 @@ module BABYLON {
          *  If the drag behavior will react to drag events (Default: true)
          */
         public enabled = true;
+        /**
+         * If camera controls should be detached during the drag
+         */
+        public detachCameraControls = true;
         
         /**
          * If set, the drag plane/axis will be rotated based on the attached mesh's world rotation (Default: true)
@@ -122,6 +126,10 @@ module BABYLON {
                     PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
                     PointerDragBehavior._planeScene.detachControl();
                     this._scene.getEngine().scenes.pop();
+                    this._scene.onDisposeObservable.addOnce(()=>{
+                        PointerDragBehavior._planeScene.dispose();
+                        (<any>PointerDragBehavior._planeScene) = null;
+                    })
                 }
             }
             this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
@@ -136,6 +144,7 @@ module BABYLON {
                 return this._attachedNode == m || m.isDescendantOf(this._attachedNode)
             }
 
+            var attachedElement:Nullable<HTMLElement> = null;
             this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState)=>{
                 if(!this.enabled){
                     return;
@@ -152,11 +161,26 @@ module BABYLON {
                             this.lastDragPosition.copyFrom(pickedPoint);
                             this.onDragStartObservable.notifyObservers({dragPlanePoint: pickedPoint, pointerId: this.currentDraggingPointerID});
                             targetPosition.copyFrom((<Mesh>this._attachedNode).absolutePosition)
+
+                            // Detatch camera controls
+                            if(this.detachCameraControls && this._scene.activeCamera){
+                                if(this._scene.activeCamera.inputs.attachedElement){
+                                    attachedElement = this._scene.activeCamera.inputs.attachedElement;
+                                    this._scene.activeCamera.detachControl(this._scene.activeCamera.inputs.attachedElement);
+                                }else{
+                                    attachedElement = null;
+                                }
+                            }
                         }
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
                         this.releaseDrag();
+
+                        // Reattach camera controls
+                        if(this.detachCameraControls && attachedElement && this._scene.activeCamera){
+                            this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){

+ 20 - 1
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -29,6 +29,10 @@ module BABYLON {
          * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
          */
         public currentDraggingPointerID = -1;
+        /**
+         * If camera controls should be detached during the drag
+         */
+        public detachCameraControls = true;
 
 
         constructor(){
@@ -70,7 +74,7 @@ module BABYLON {
             var pickPredicate = (m:AbstractMesh)=>{
                 return this._ownerNode == m || m.isDescendantOf(this._ownerNode);
             }
-            
+            var attachedElement:Nullable<HTMLElement> = null;
             this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState)=>{                
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if(!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)){
@@ -101,6 +105,16 @@ module BABYLON {
                         this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
                         this.dragging = true;
                         this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
+
+                        // Detatch camera controls
+                        if(this.detachCameraControls && this._scene.activeCamera){
+                            if(this._scene.activeCamera.inputs.attachedElement){
+                                attachedElement = this._scene.activeCamera.inputs.attachedElement;
+                                this._scene.activeCamera.detachControl(this._scene.activeCamera.inputs.attachedElement);
+                            }else{
+                                attachedElement = null;
+                            }
+                        }
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
@@ -109,6 +123,11 @@ module BABYLON {
                         this.currentDraggingPointerID = -1;
                         pickedMesh = null;
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
+                        
+                        // Reattach camera controls
+                        if(this.detachCameraControls && attachedElement && this._scene.activeCamera){
+                            this._scene.activeCamera.attachControl(attachedElement, true);
+                        }
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh){

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -430,7 +430,7 @@
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "3.3.0-alpha.11";
+            return "3.3.0-alpha.12";
         }
 
         // Updatable statics so stick with vars here

+ 6 - 0
src/Helpers/babylon.photoDome.ts

@@ -82,6 +82,8 @@ module BABYLON {
                 this._useDirectMapping = options.useDirectMapping;            
             }
 
+            this._setReady(false);
+
             // create
             let material = this._material = new BackgroundMaterial(name + "_material", scene);
             this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
@@ -92,6 +94,10 @@ module BABYLON {
             material.fovMultiplier = 1.0;
 
             this.photoTexture = new Texture(urlOfPhoto, scene, true, !this._useDirectMapping);
+
+            this.photoTexture.onLoadObservable.addOnce(()=> {
+                this._setReady(true);
+            }) ;             
            
             // configure mesh
             this._mesh.material = material;

+ 6 - 0
src/Helpers/babylon.videoDome.ts

@@ -72,12 +72,18 @@ module BABYLON {
                 this._useDirectMapping = options.useDirectMapping;            
             }
 
+            this._setReady(false);
+
             // create
             let tempOptions: VideoTextureSettings = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
             let material = this._material = new BackgroundMaterial(name + "_material", scene);
             let texture = this._videoTexture = new VideoTexture(name + "_texture", urlsOrVideo, scene, false, this._useDirectMapping, Texture.TRILINEAR_SAMPLINGMODE, tempOptions);
             this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
 
+            texture.onLoadObservable.addOnce(()=> {
+                this._setReady(true);
+            }) ;          
+
             // configure material
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;

+ 2 - 0
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -197,6 +197,7 @@
 
             // Rendering groups
             this._renderingManager = new RenderingManager(scene);
+            this._renderingManager._useSceneAutoClearSetup = true;
 
             if (isMulti) {
                 return;
@@ -671,6 +672,7 @@
          */
         public setRenderingAutoClearDepthStencil(renderingGroupId: number, autoClearDepthStencil: boolean): void {
             this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
+            this._renderingManager._useSceneAutoClearSetup = false;
         }
 
         public clone(): RenderTargetTexture {

+ 9 - 0
src/Mesh/babylon.linesMesh.ts

@@ -74,14 +74,23 @@
             return "LinesMesh";
         }
 
+        /**
+         * @hidden
+         */
         public get material(): Material {
             return this._colorShader;
         }
 
+        /**
+         * @hidden
+         */
         public set material(value: Material) {
             // Do nothing
         }
 
+        /**
+         * @hidden
+         */
         public get checkCollisions(): boolean {
             return false;
         }

+ 1 - 1
src/PostProcess/babylon.postProcess.ts

@@ -468,7 +468,7 @@
             this.onActivateObservable.notifyObservers(camera);
 
             // Clear
-            if (this.autoClear && this.alphaMode === Engine.ALPHA_DISABLE) {
+            if (scene._allowPostProcessClear && this.autoClear && this.alphaMode === Engine.ALPHA_DISABLE) {
                 this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
             }
 

+ 50 - 46
src/Rendering/babylon.renderingManager.ts

@@ -4,9 +4,18 @@
      * Interface describing the different options available in the rendering manager
      * regarding Auto Clear between groups.
      */
-    interface RenderingManageAutoClearOptions {
+    export interface IRenderingManagerAutoClearSetup {
+        /**
+         * Defines whether or not autoclear is enable.
+         */
         autoClear: boolean;
+        /**
+         * Defines whether or not to autoclear the depth buffer.
+         */
         depth: boolean;
+        /**
+         * Defines whether or not to autoclear the stencil buffer.
+         */
         stencil: boolean;
     }
 
@@ -26,15 +35,20 @@
          */
         public static AUTOCLEAR = true;
 
+        /**
+         * Hidden
+         */
+        public _useSceneAutoClearSetup = false;
+
         private _scene: Scene;
         private _renderingGroups = new Array<RenderingGroup>();
         private _depthStencilBufferAlreadyCleaned: boolean;
-
-        private _autoClearDepthStencil: { [id: number]: RenderingManageAutoClearOptions } = {};
+        
+        private _autoClearDepthStencil: { [id: number]: IRenderingManagerAutoClearSetup } = {};
         private _customOpaqueSortCompareFn: { [id: number]: Nullable<(a: SubMesh, b: SubMesh) => number> } = {};
         private _customAlphaTestSortCompareFn: { [id: number]: Nullable<(a: SubMesh, b: SubMesh) => number> } = {};
         private _customTransparentSortCompareFn: { [id: number]: Nullable<(a: SubMesh, b: SubMesh) => number> } = {};
-        private _renderinGroupInfo: Nullable<RenderingGroupInfo> = null;
+        private _renderingGroupInfo: Nullable<RenderingGroupInfo> = new RenderingGroupInfo();
 
         constructor(scene: Scene) {
             this._scene = scene;
@@ -56,17 +70,10 @@
         public render(customRenderFunction: Nullable<(opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>) => void>,
             activeMeshes: Nullable<AbstractMesh[]>, renderParticles: boolean, renderSprites: boolean): void {
 
-            // Check if there's at least on observer on the onRenderingGroupObservable and initialize things to fire it
-            let observable = this._scene.onRenderingGroupObservable.hasObservers() ? this._scene.onRenderingGroupObservable : null;
-            let info: Nullable<RenderingGroupInfo> = null;
-            if (observable) {
-                if (!this._renderinGroupInfo) {
-                    this._renderinGroupInfo = new RenderingGroupInfo();
-                }
-                info = this._renderinGroupInfo;
-                info.scene = this._scene;
-                info.camera = this._scene.activeCamera;
-            }
+            // Update the observable context (not null as it only goes away on dispose)
+            const info = this._renderingGroupInfo!;
+            info.scene = this._scene;
+            info.camera = this._scene.activeCamera;
 
             // Dispatch sprites
             if (renderSprites) {
@@ -80,51 +87,37 @@
             for (let index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
                 this._depthStencilBufferAlreadyCleaned = index === RenderingManager.MIN_RENDERINGGROUPS;
                 var renderingGroup = this._renderingGroups[index];
-                if (!renderingGroup && !observable)
+                if (!renderingGroup)
                     continue;
 
-                let renderingGroupMask = 0;
+                let renderingGroupMask = Math.pow(2, index);
+                info.renderingGroupId = index;
 
-                // Fire PRECLEAR stage
-                if (observable && info) {
-                    renderingGroupMask = Math.pow(2, index);
-                    info.renderStage = RenderingGroupInfo.STAGE_PRECLEAR;
-                    info.renderingGroupId = index;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
+                // Before Observable
+                this._scene.onBeforeRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
 
                 // Clear depth/stencil if needed
                 if (RenderingManager.AUTOCLEAR) {
-                    let autoClear = this._autoClearDepthStencil[index];
+                    const autoClear = this._useSceneAutoClearSetup ? 
+                        this._scene.getAutoClearDepthStencilSetup(index) :
+                        this._autoClearDepthStencil[index];
+
                     if (autoClear && autoClear.autoClear) {
                         this._clearDepthStencilBuffer(autoClear.depth, autoClear.stencil);
                     }
                 }
 
-                if (observable && info) {
-                    // Fire PREOPAQUE stage
-                    info.renderStage = RenderingGroupInfo.STAGE_PREOPAQUE;
-                    observable.notifyObservers(info, renderingGroupMask);
-                    // Fire PRETRANSPARENT stage
-                    info.renderStage = RenderingGroupInfo.STAGE_PRETRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
+                // Render
+                for (let step of this._scene._beforeRenderingGroupDrawStage) {
+                    step.action(index);
                 }
-
-                if (renderingGroup) {
-                    for (let step of this._scene._beforeRenderingGroupDrawStage) {
-                        step.action(index);
-                    }
-                    renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
-                    for (let step of this._scene._afterRenderingGroupDrawStage) {
-                        step.action(index);
-                    }
+                renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
+                for (let step of this._scene._afterRenderingGroupDrawStage) {
+                    step.action(index);
                 }
 
-                // Fire POSTTRANSPARENT stage
-                if (observable && info) {
-                    info.renderStage = RenderingGroupInfo.STAGE_POSTTRANSPARENT;
-                    observable.notifyObservers(info, renderingGroupMask);
-                }
+                // After Observable
+                this._scene.onAfterRenderingGroupObservable.notifyObservers(info, renderingGroupMask);
             }
         }
 
@@ -140,6 +133,7 @@
         public dispose(): void {
             this.freeRenderingGroups();
             this._renderingGroups.length = 0;
+            this._renderingGroupInfo = null;
         }
 
         /**
@@ -239,5 +233,15 @@
                 stencil: stencil
             };
         }
+
+        /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        public getAutoClearDepthStencilSetup(index: number): IRenderingManagerAutoClearSetup {
+            return this._autoClearDepthStencil[index];
+        }
     }
 } 

+ 25 - 1
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -53,6 +53,7 @@ module BABYLON {
         constructor(/** the original scene that will be rendered on top of */ public originalScene:Scene){
             // 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._allowPostProcessClear = false;
             originalScene.getEngine().scenes.pop();
 
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority
@@ -171,7 +172,30 @@ module BABYLON {
          */
         public render(){
             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;
+                }
+            }
+            
         }
 
         /**

+ 5 - 0
src/Tools/babylon.tools.ts

@@ -1121,6 +1121,11 @@
          * @param fileName defines the name of the downloaded file
          */
         public static Download(blob: Blob, fileName: string): void {
+            if (navigator && navigator.msSaveBlob) {
+                navigator.msSaveBlob(blob, fileName);
+                return;
+            }
+
             var url = window.URL.createObjectURL(blob);
             var a = document.createElement("a");
             document.body.appendChild(a);

+ 2 - 2
src/babylon.node.ts

@@ -96,8 +96,8 @@
 
         /** @hidden */
         public _waitingParentId: Nullable<string>;
-
-        private _scene: Scene;
+        /** @hidden */
+        public _scene: Scene;
         /** @hidden */
         public _cache: any;
 

+ 23 - 32
src/babylon.scene.ts

@@ -77,35 +77,6 @@
          * The ID of the renderingGroup being processed
          */
         renderingGroupId: number;
-
-        /**
-         * The rendering stage, can be either STAGE_PRECLEAR, STAGE_PREOPAQUE, STAGE_PRETRANSPARENT, STAGE_POSTTRANSPARENT
-         */
-        renderStage: number;
-
-        /**
-         * Stage corresponding to the very first hook in the renderingGroup phase: before the render buffer may be cleared
-         * This stage will be fired no matter what
-         */
-        static STAGE_PRECLEAR = 1;
-
-        /**
-         * Called before opaque object are rendered.
-         * This stage will be fired only if there's 3D Opaque content to render
-         */
-        static STAGE_PREOPAQUE = 2;
-
-        /**
-         * Called after the opaque objects are rendered and before the transparent ones
-         * This stage will be fired only if there's 3D transparent content to render
-         */
-        static STAGE_PRETRANSPARENT = 3;
-
-        /**
-         * Called after the transparent object are rendered, last hook of the renderingGroup phase
-         * This stage will be fired no matter what
-         */
-        static STAGE_POSTTRANSPARENT = 4;
     }
 
     /**
@@ -532,11 +503,18 @@
         public onAfterStepObservable = new Observable<Scene>();
 
         /**
-         * This Observable will be triggered for each stage of each renderingGroup of each rendered camera.
+         * This Observable will be triggered before rendering each renderingGroup of each rendered camera.
+         * The RenderinGroupInfo class contains all the information about the context in which the observable is called
+         * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
+         */
+        public onBeforeRenderingGroupObservable = new Observable<RenderingGroupInfo>();
+
+        /**
+         * This Observable will be triggered after rendering each renderingGroup of each rendered camera.
          * The RenderinGroupInfo class contains all the information about the context in which the observable is called
          * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
          */
-        public onRenderingGroupObservable = new Observable<RenderingGroupInfo>();
+        public onAfterRenderingGroupObservable = new Observable<RenderingGroupInfo>();
 
         // Animations
         private _registeredForLateAnimationBindings = new SmartArrayNoDuplicate<any>(256);
@@ -4333,7 +4311,8 @@
         public updateAlternateTransformMatrix(alternateCamera: Camera): void {
             this._setAlternateTransformMatrix(alternateCamera.getViewMatrix(), alternateCamera.getProjectionMatrix());
         }
-
+        /** @hidden */
+        public _allowPostProcessClear = true;
         private _renderForCamera(camera: Camera, rigParent?: Camera): void {
             if (camera && camera._skipRendering) {
                 return;
@@ -5067,6 +5046,8 @@
             this.onBeforeAnimationsObservable.clear();
             this.onAfterAnimationsObservable.clear();
             this.onDataLoadedObservable.clear();
+            this.onBeforeRenderingGroupObservable.clear();
+            this.onAfterRenderingGroupObservable.clear();
 
             this.detachControl();
 
@@ -6006,6 +5987,16 @@
         }
 
         /**
+         * Gets the current auto clear configuration for one rendering group of the rendering
+         * manager.
+         * @param index the rendering group index to get the information for
+         * @returns The auto clear setup for the requested rendering group
+         */
+        public getAutoClearDepthStencilSetup(index: number): IRenderingManagerAutoClearSetup {
+            return this._renderingManager.getAutoClearDepthStencilSetup(index);
+        }
+
+        /**
          * Will flag all materials as dirty to trigger new shader compilation
          * @param flag defines the flag used to specify which material part must be marked as dirty
          * @param predicate If not null, it will be used to specifiy if a material has to be marked as dirty

BIN
tests/validation/ReferenceImages/spherepanel.png


+ 0 - 0
tests/validation/config.json


Някои файлове не бяха показани, защото твърде много файлове са промени