sebastien 6 years ago
parent
commit
b2827a46ba
51 changed files with 8232 additions and 6451 deletions
  1. 4303 4046
      Playground/babylon.d.txt
  2. BIN
      Playground/textures/gui/backgroundImage.png
  3. BIN
      Playground/textures/gui/thumb.png
  4. BIN
      Playground/textures/gui/valueImage.png
  5. 3 1
      Tools/Gulp/config.json
  6. 1981 1890
      dist/preview release/babylon.d.ts
  7. 1 1
      dist/preview release/babylon.js
  8. 348 100
      dist/preview release/babylon.max.js
  9. 348 100
      dist/preview release/babylon.no-module.max.js
  10. 1 1
      dist/preview release/babylon.worker.js
  11. 350 102
      dist/preview release/es6.js
  12. 1 1
      dist/preview release/glTF2Interface/package.json
  13. 1 1
      dist/preview release/gui/babylon.gui.js
  14. 1 1
      dist/preview release/gui/babylon.gui.js.map
  15. 1 1
      dist/preview release/gui/babylon.gui.min.js
  16. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  17. 2 2
      dist/preview release/gui/package.json
  18. 5 5
      dist/preview release/inspector/package.json
  19. 3 3
      dist/preview release/loaders/package.json
  20. 2 2
      dist/preview release/materialsLibrary/package.json
  21. 2 2
      dist/preview release/postProcessesLibrary/package.json
  22. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  23. 128 0
      dist/preview release/serializers/babylon.glTF2Serializer.js
  24. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  25. 128 0
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  26. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js.map
  27. 25 0
      dist/preview release/serializers/babylonjs.serializers.d.ts
  28. 128 0
      dist/preview release/serializers/babylonjs.serializers.js
  29. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  30. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  31. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js.map
  32. 60 0
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  33. 3 3
      dist/preview release/serializers/package.json
  34. 5 5
      dist/preview release/viewer/babylon.viewer.d.ts
  35. 1 1
      dist/preview release/viewer/babylon.viewer.js
  36. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  37. 5 5
      dist/preview release/viewer/babylon.viewer.module.d.ts
  38. 5 3
      dist/preview release/what's new.md
  39. 1 1
      gui/src/2D/controls/imageBasedSlider.ts
  40. 1 1
      package.json
  41. 1 1
      readme.md
  42. 2 7
      src/Cameras/Inputs/babylon.arcRotateCameraVRDeviceOrientationInput.ts
  43. 87 0
      src/Cameras/XR/babylon.webXREnterExitUI.ts
  44. 44 35
      src/Cameras/XR/babylon.webXRExperienceHelper.ts
  45. 62 0
      src/Cameras/XR/babylon.webXRManagedOutputCanvas.ts
  46. 15 2
      src/Cameras/XR/babylon.webXRSessionManager.ts
  47. 1 1
      src/Cameras/babylon.arcRotateCamera.ts
  48. 6 5
      src/Engine/babylon.engine.ts
  49. 16 0
      src/Helpers/babylon.sceneHelpers.ts
  50. 142 112
      src/Math/babylon.math.ts
  51. 4 1
      src/babylon.mixins.ts

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


BIN
Playground/textures/gui/backgroundImage.png


BIN
Playground/textures/gui/thumb.png


BIN
Playground/textures/gui/valueImage.png


+ 3 - 1
Tools/Gulp/config.json

@@ -1303,7 +1303,9 @@
                 "../../src/Cameras/VR/babylon.vrExperienceHelper.js",
                 "../../src/Cameras/VR/babylon.vrExperienceHelper.js",
                 "../../src/Cameras/XR/babylon.webXRCamera.js",
                 "../../src/Cameras/XR/babylon.webXRCamera.js",
                 "../../src/Cameras/XR/babylon.webXRSessionManager.js",
                 "../../src/Cameras/XR/babylon.webXRSessionManager.js",
-                "../../src/Cameras/XR/babylon.webXRExperienceHelper.js"
+                "../../src/Cameras/XR/babylon.webXRExperienceHelper.js",
+                "../../src/Cameras/XR/babylon.webXREnterExitUI.js",
+                "../../src/Cameras/XR/babylon.webXRManagedOutputCanvas.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
                 "core",
                 "core",

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


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


+ 348 - 100
dist/preview release/babylon.max.js

@@ -7503,16 +7503,30 @@ var BABYLON;
                 return 1;
                 return 1;
             }
             }
             var m = this._m;
             var m = this._m;
-            var temp1 = m[10] * m[15] - m[11] * m[14];
-            var temp2 = m[9] * m[15] - m[11] * m[13];
-            var temp3 = m[9] * m[14] - m[10] * m[13];
-            var temp4 = m[8] * m[15] - m[11] * m[12];
-            var temp5 = m[8] * m[14] - m[10] * m[12];
-            var temp6 = m[8] * m[13] - m[9] * m[12];
-            return (m[0] * (m[5] * temp1 - m[6] * temp2 + m[7] * temp3) -
-                m[1] * (m[4] * temp1 - m[6] * temp4 + m[7] * temp5) +
-                m[2] * (m[4] * temp2 - m[5] * temp4 + m[7] * temp6) -
-                m[3] * (m[4] * temp3 - m[5] * temp5 + m[6] * temp6));
+            var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
+            var m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
+            var m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
+            var m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
+            // https://en.wikipedia.org/wiki/Laplace_expansion
+            // to compute the deterrminant of a 4x4 Matrix we compute the cofactors of any row or column,
+            // then we multiply each Cofactor by its corresponding matrix value and sum them all to get the determinant
+            // Cofactor(i, j) = sign(i,j) * det(Minor(i, j))
+            // where
+            //  - sign(i,j) = (i+j) % 2 === 0 ? 1 : -1
+            //  - Minor(i, j) is the 3x3 matrix we get by removing row i and column j from current Matrix
+            //
+            // Here we do that for the 1st row.
+            var det_22_33 = m22 * m33 - m32 * m23;
+            var det_21_33 = m21 * m33 - m31 * m23;
+            var det_21_32 = m21 * m32 - m31 * m22;
+            var det_20_33 = m20 * m33 - m30 * m23;
+            var det_20_32 = m20 * m32 - m22 * m30;
+            var det_20_31 = m20 * m31 - m30 * m21;
+            var cofact_00 = +(m11 * det_22_33 - m12 * det_21_33 + m13 * det_21_32);
+            var cofact_01 = -(m10 * det_22_33 - m12 * det_20_33 + m13 * det_20_32);
+            var cofact_02 = +(m10 * det_21_33 - m11 * det_20_33 + m13 * det_20_31);
+            var cofact_03 = -(m10 * det_21_32 - m11 * det_20_32 + m12 * det_20_31);
+            return m00 * cofact_00 + m01 * cofact_01 + m02 * cofact_02 + m03 * cofact_03;
         };
         };
         // Methods
         // Methods
         /**
         /**
@@ -7591,52 +7605,54 @@ var BABYLON;
                 Matrix.IdentityToRef(other);
                 Matrix.IdentityToRef(other);
                 return this;
                 return this;
             }
             }
+            // the inverse of a Matrix is the transpose of cofactor matrix divided by the determinant
             var m = this._m;
             var m = this._m;
-            var l1 = m[0], l2 = m[1], l3 = m[2], l4 = m[3];
-            var l5 = m[4], l6 = m[5], l7 = m[6], l8 = m[7];
-            var l9 = m[8], l10 = m[9], l11 = m[10], l12 = m[11];
-            var l13 = m[12], l14 = m[13], l15 = m[14], l16 = m[15];
-            var l17 = l11 * l16 - l12 * l15;
-            var l18 = l10 * l16 - l12 * l14;
-            var l19 = l10 * l15 - l11 * l14;
-            var l20 = l9 * l16 - l12 * l13;
-            var l21 = l9 * l15 - l11 * l13;
-            var l22 = l9 * l14 - l10 * l13;
-            var l23 = l6 * l17 - l7 * l18 + l8 * l19;
-            var l24 = -(l5 * l17 - l7 * l20 + l8 * l21);
-            var l25 = l5 * l18 - l6 * l20 + l8 * l22;
-            var l26 = -(l5 * l19 - l6 * l21 + l7 * l22);
-            var l27 = 1 / (l1 * l23 + l2 * l24 + l3 * l25 + l4 * l26);
-            var l28 = l7 * l16 - l8 * l15;
-            var l29 = l6 * l16 - l8 * l14;
-            var l30 = l6 * l15 - l7 * l14;
-            var l31 = l5 * l16 - l8 * l13;
-            var l32 = l5 * l15 - l7 * l13;
-            var l33 = l5 * l14 - l6 * l13;
-            var l34 = l7 * l12 - l8 * l11;
-            var l35 = l6 * l12 - l8 * l10;
-            var l36 = l6 * l11 - l7 * l10;
-            var l37 = l5 * l12 - l8 * l9;
-            var l38 = l5 * l11 - l7 * l9;
-            var l39 = l5 * l10 - l6 * l9;
-            var otherM = other._m;
-            otherM[0] = l23 * l27;
-            otherM[4] = l24 * l27;
-            otherM[8] = l25 * l27;
-            otherM[12] = l26 * l27;
-            otherM[1] = -(l2 * l17 - l3 * l18 + l4 * l19) * l27;
-            otherM[5] = (l1 * l17 - l3 * l20 + l4 * l21) * l27;
-            otherM[9] = -(l1 * l18 - l2 * l20 + l4 * l22) * l27;
-            otherM[13] = (l1 * l19 - l2 * l21 + l3 * l22) * l27;
-            otherM[2] = (l2 * l28 - l3 * l29 + l4 * l30) * l27;
-            otherM[6] = -(l1 * l28 - l3 * l31 + l4 * l32) * l27;
-            otherM[10] = (l1 * l29 - l2 * l31 + l4 * l33) * l27;
-            otherM[14] = -(l1 * l30 - l2 * l32 + l3 * l33) * l27;
-            otherM[3] = -(l2 * l34 - l3 * l35 + l4 * l36) * l27;
-            otherM[7] = (l1 * l34 - l3 * l37 + l4 * l38) * l27;
-            otherM[11] = -(l1 * l35 - l2 * l37 + l4 * l39) * l27;
-            otherM[15] = (l1 * l36 - l2 * l38 + l3 * l39) * l27;
-            other._markAsUpdated();
+            var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
+            var m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
+            var m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
+            var m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
+            var det_22_33 = m22 * m33 - m32 * m23;
+            var det_21_33 = m21 * m33 - m31 * m23;
+            var det_21_32 = m21 * m32 - m31 * m22;
+            var det_20_33 = m20 * m33 - m30 * m23;
+            var det_20_32 = m20 * m32 - m22 * m30;
+            var det_20_31 = m20 * m31 - m30 * m21;
+            var cofact_00 = +(m11 * det_22_33 - m12 * det_21_33 + m13 * det_21_32);
+            var cofact_01 = -(m10 * det_22_33 - m12 * det_20_33 + m13 * det_20_32);
+            var cofact_02 = +(m10 * det_21_33 - m11 * det_20_33 + m13 * det_20_31);
+            var cofact_03 = -(m10 * det_21_32 - m11 * det_20_32 + m12 * det_20_31);
+            var det = m00 * cofact_00 + m01 * cofact_01 + m02 * cofact_02 + m03 * cofact_03;
+            if (det === 0) {
+                // not invertible
+                other.copyFrom(this);
+                return this;
+            }
+            var detInv = 1 / det;
+            var det_12_33 = m12 * m33 - m32 * m13;
+            var det_11_33 = m11 * m33 - m31 * m13;
+            var det_11_32 = m11 * m32 - m31 * m12;
+            var det_10_33 = m10 * m33 - m30 * m13;
+            var det_10_32 = m10 * m32 - m30 * m12;
+            var det_10_31 = m10 * m31 - m30 * m11;
+            var det_12_23 = m12 * m23 - m22 * m13;
+            var det_11_23 = m11 * m23 - m21 * m13;
+            var det_11_22 = m11 * m22 - m21 * m12;
+            var det_10_23 = m10 * m23 - m20 * m13;
+            var det_10_22 = m10 * m22 - m20 * m12;
+            var det_10_21 = m10 * m21 - m20 * m11;
+            var cofact_10 = -(m01 * det_22_33 - m02 * det_21_33 + m03 * det_21_32);
+            var cofact_11 = +(m00 * det_22_33 - m02 * det_20_33 + m03 * det_20_32);
+            var cofact_12 = -(m00 * det_21_33 - m01 * det_20_33 + m03 * det_20_31);
+            var cofact_13 = +(m00 * det_21_32 - m01 * det_20_32 + m02 * det_20_31);
+            var cofact_20 = +(m01 * det_12_33 - m02 * det_11_33 + m03 * det_11_32);
+            var cofact_21 = -(m00 * det_12_33 - m02 * det_10_33 + m03 * det_10_32);
+            var cofact_22 = +(m00 * det_11_33 - m01 * det_10_33 + m03 * det_10_31);
+            var cofact_23 = -(m00 * det_11_32 - m01 * det_10_32 + m02 * det_10_31);
+            var cofact_30 = -(m01 * det_12_23 - m02 * det_11_23 + m03 * det_11_22);
+            var cofact_31 = +(m00 * det_12_23 - m02 * det_10_23 + m03 * det_10_22);
+            var cofact_32 = -(m00 * det_11_23 - m01 * det_10_23 + m03 * det_10_21);
+            var cofact_33 = +(m00 * det_11_22 - m01 * det_10_22 + m02 * det_10_21);
+            Matrix.FromValuesToRef(cofact_00 * detInv, cofact_10 * detInv, cofact_20 * detInv, cofact_30 * detInv, cofact_01 * detInv, cofact_11 * detInv, cofact_21 * detInv, cofact_31 * detInv, cofact_02 * detInv, cofact_12 * detInv, cofact_22 * detInv, cofact_32 * detInv, cofact_03 * detInv, cofact_13 * detInv, cofact_23 * detInv, cofact_33 * detInv, other);
             return this;
             return this;
         };
         };
         /**
         /**
@@ -8335,6 +8351,9 @@ var BABYLON;
             m[9] = (axis.z * axis.y) * c1 + (axis.x * s);
             m[9] = (axis.z * axis.y) * c1 + (axis.x * s);
             m[10] = (axis.z * axis.z) * c1 + c;
             m[10] = (axis.z * axis.z) * c1 + c;
             m[11] = 0.0;
             m[11] = 0.0;
+            m[12] = 0.0;
+            m[13] = 0.0;
+            m[14] = 0.0;
             m[15] = 1.0;
             m[15] = 1.0;
             result._markAsUpdated();
             result._markAsUpdated();
         };
         };
@@ -12684,7 +12703,7 @@ var BABYLON;
              * Returns the current version of the framework
              * Returns the current version of the framework
              */
              */
             get: function () {
             get: function () {
-                return "4.0.0-alpha.2";
+                return "4.0.0-alpha.4";
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -12922,7 +12941,7 @@ var BABYLON;
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
-            this._caps.highPrecisionShaderSupported = true;
+            this._caps.highPrecisionShaderSupported = false;
             this._caps.timerQuery = this._gl.getExtension('EXT_disjoint_timer_query_webgl2') || this._gl.getExtension("EXT_disjoint_timer_query");
             this._caps.timerQuery = this._gl.getExtension('EXT_disjoint_timer_query_webgl2') || this._gl.getExtension("EXT_disjoint_timer_query");
             if (this._caps.timerQuery) {
             if (this._caps.timerQuery) {
                 if (this._webGLVersion === 1) {
                 if (this._webGLVersion === 1) {
@@ -13030,9 +13049,10 @@ var BABYLON;
                 this.texturesSupported.push('-etc1.ktx');
                 this.texturesSupported.push('-etc1.ktx');
             }
             }
             if (this._gl.getShaderPrecisionFormat) {
             if (this._gl.getShaderPrecisionFormat) {
-                var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
-                if (highp) {
-                    this._caps.highPrecisionShaderSupported = highp.precision !== 0;
+                var vertex_highp = this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER, this._gl.HIGH_FLOAT);
+                var fragment_highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
+                if (vertex_highp && fragment_highp) {
+                    this._caps.highPrecisionShaderSupported = vertex_highp.precision !== 0 && fragment_highp.precision !== 0;
                 }
                 }
             }
             }
             // Depth buffer
             // Depth buffer
@@ -51818,7 +51838,7 @@ var BABYLON;
         };
         };
         ArcRotateCamera.prototype._getTargetPosition = function () {
         ArcRotateCamera.prototype._getTargetPosition = function () {
             if (this._targetHost && this._targetHost.getAbsolutePosition) {
             if (this._targetHost && this._targetHost.getAbsolutePosition) {
-                var pos = this._targetHost.getAbsolutePosition();
+                var pos = this._targetHost.absolutePosition;
                 if (this._targetBoundingCenter) {
                 if (this._targetBoundingCenter) {
                     pos.addToRef(this._targetBoundingCenter, this._target);
                     pos.addToRef(this._targetBoundingCenter, this._target);
                 }
                 }
@@ -105772,10 +105792,6 @@ var BABYLON;
              */
              */
             this.alphaCorrection = 1;
             this.alphaCorrection = 1;
             /**
             /**
-             * Defines a correction factor applied on the beta value retrieved from the orientation events.
-             */
-            this.betaCorrection = 1;
-            /**
              * Defines a correction factor applied on the gamma value retrieved from the orientation events.
              * Defines a correction factor applied on the gamma value retrieved from the orientation events.
              */
              */
             this.gammaCorrection = 1;
             this.gammaCorrection = 1;
@@ -105796,10 +105812,10 @@ var BABYLON;
         /** @hidden */
         /** @hidden */
         ArcRotateCameraVRDeviceOrientationInput.prototype._onOrientationEvent = function (evt) {
         ArcRotateCameraVRDeviceOrientationInput.prototype._onOrientationEvent = function (evt) {
             if (evt.alpha !== null) {
             if (evt.alpha !== null) {
-                this._alpha = +evt.alpha | 0;
+                this._alpha = (+evt.alpha | 0) * this.alphaCorrection;
             }
             }
             if (evt.gamma !== null) {
             if (evt.gamma !== null) {
-                this._gamma = +evt.gamma | 0;
+                this._gamma = (+evt.gamma | 0) * this.gammaCorrection;
             }
             }
             this._dirty = true;
             this._dirty = true;
         };
         };
@@ -108589,7 +108605,7 @@ var BABYLON;
             this._tmpMatrix = new BABYLON.Matrix();
             this._tmpMatrix = new BABYLON.Matrix();
         }
         }
         /**
         /**
-         * Initializes the manager, this must be done with a user action (eg. button click event)
+         * Initializes the manager
          * After initialization enterXR can be called to start an XR session
          * After initialization enterXR can be called to start an XR session
          * @returns Promise which resolves after it is initialized
          * @returns Promise which resolves after it is initialized
          */
          */
@@ -108607,7 +108623,7 @@ var BABYLON;
             });
             });
         };
         };
         /**
         /**
-         * Enters XR with the desired XR session options
+         * Enters XR with the desired XR session options, this must be done with a user action (eg. button click event)
          * @param sessionCreationOptions xr options to create the session with
          * @param sessionCreationOptions xr options to create the session with
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @returns Promise which resolves after it enters XR
          * @returns Promise which resolves after it enters XR
@@ -108691,6 +108707,18 @@ var BABYLON;
             });
             });
         };
         };
         /**
         /**
+         * Checks if a session would be supported for the creation options specified
+         * @param options creation options to check if they are supported
+         * @returns true if supported
+         */
+        WebXRSessionManager.prototype.supportsSession = function (options) {
+            return this._xrDevice.supportsSession(options).then(function () {
+                return true;
+            }).catch(function (e) {
+                return false;
+            });
+        };
+        /**
          * @hidden
          * @hidden
          * Converts the render layer of xrSession to a render target
          * Converts the render layer of xrSession to a render target
          * @param session session to create render target for
          * @param session session to create render target for
@@ -108729,17 +108757,21 @@ var BABYLON;
     var WebXRState;
     var WebXRState;
     (function (WebXRState) {
     (function (WebXRState) {
         /**
         /**
-         * Transitioning to/from being in XR mode
+         * Transitioning to being in XR mode
+         */
+        WebXRState[WebXRState["ENTERING_XR"] = 0] = "ENTERING_XR";
+        /**
+         * Transitioning to non XR mode
          */
          */
-        WebXRState[WebXRState["TRANSITION"] = 0] = "TRANSITION";
+        WebXRState[WebXRState["EXITING_XR"] = 1] = "EXITING_XR";
         /**
         /**
          * In XR mode and presenting
          * In XR mode and presenting
          */
          */
-        WebXRState[WebXRState["IN_XR"] = 1] = "IN_XR";
+        WebXRState[WebXRState["IN_XR"] = 2] = "IN_XR";
         /**
         /**
          * Not entered XR mode
          * Not entered XR mode
          */
          */
-        WebXRState[WebXRState["NOT_IN_XR"] = 2] = "NOT_IN_XR";
+        WebXRState[WebXRState["NOT_IN_XR"] = 3] = "NOT_IN_XR";
     })(WebXRState = BABYLON.WebXRState || (BABYLON.WebXRState = {}));
     })(WebXRState = BABYLON.WebXRState || (BABYLON.WebXRState = {}));
     /**
     /**
      * Helper class used to enable XR
      * Helper class used to enable XR
@@ -108762,18 +108794,35 @@ var BABYLON;
             this.onStateChangedObservable = new BABYLON.Observable();
             this.onStateChangedObservable = new BABYLON.Observable();
             this._nonVRCamera = null;
             this._nonVRCamera = null;
             this._originalSceneAutoClear = true;
             this._originalSceneAutoClear = true;
+            this._supported = false;
             this.camera = new BABYLON.WebXRCamera("", scene);
             this.camera = new BABYLON.WebXRCamera("", scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this.container = new BABYLON.AbstractMesh("", scene);
             this.container = new BABYLON.AbstractMesh("", scene);
-            this._sessionManager.initialize();
         }
         }
+        WebXRExperienceHelper.prototype._setState = function (val) {
+            this.state = val;
+            this.onStateChangedObservable.notifyObservers(this.state);
+        };
+        /**
+         * Creates the experience helper
+         * @param scene the scene to attach the experience helper to
+         * @returns a promise for the experience helper
+         */
+        WebXRExperienceHelper.CreateAsync = function (scene) {
+            var helper = new WebXRExperienceHelper(scene);
+            return helper._sessionManager.initialize().then(function () {
+                helper._supported = true;
+                return helper;
+            }).catch(function () {
+                return helper;
+            });
+        };
         /**
         /**
          * Exits XR mode and returns the scene to its original state
          * Exits XR mode and returns the scene to its original state
          * @returns promise that resolves after xr mode has exited
          * @returns promise that resolves after xr mode has exited
          */
          */
         WebXRExperienceHelper.prototype.exitXR = function () {
         WebXRExperienceHelper.prototype.exitXR = function () {
-            this.state = WebXRState.TRANSITION;
-            this.onStateChangedObservable.notifyObservers(this.state);
+            this._setState(WebXRState.EXITING_XR);
             return this._sessionManager.exitXR();
             return this._sessionManager.exitXR();
         };
         };
         /**
         /**
@@ -108784,12 +108833,7 @@ var BABYLON;
          */
          */
         WebXRExperienceHelper.prototype.enterXR = function (sessionCreationOptions, frameOfReference) {
         WebXRExperienceHelper.prototype.enterXR = function (sessionCreationOptions, frameOfReference) {
             var _this = this;
             var _this = this;
-            this.state = WebXRState.TRANSITION;
-            this.onStateChangedObservable.notifyObservers(this.state);
-            this._createCanvas();
-            if (!sessionCreationOptions.outputContext) {
-                sessionCreationOptions.outputContext = this._outputCanvasContext;
-            }
+            this._setState(WebXRState.ENTERING_XR);
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(function () {
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(function () {
                 // Cache pre xr scene settings
                 // Cache pre xr scene settings
                 _this._originalSceneAutoClear = _this.scene.autoClear;
                 _this._originalSceneAutoClear = _this.scene.autoClear;
@@ -108809,43 +108853,238 @@ var BABYLON;
                     _this.scene.autoClear = _this._originalSceneAutoClear;
                     _this.scene.autoClear = _this._originalSceneAutoClear;
                     _this.scene.activeCamera = _this._nonVRCamera;
                     _this.scene.activeCamera = _this._nonVRCamera;
                     _this._sessionManager.onXRFrameObservable.clear();
                     _this._sessionManager.onXRFrameObservable.clear();
-                    _this._removeCanvas();
-                    _this.state = WebXRState.NOT_IN_XR;
-                    _this.onStateChangedObservable.notifyObservers(_this.state);
+                    _this._setState(WebXRState.NOT_IN_XR);
                 });
                 });
-                _this.state = WebXRState.IN_XR;
-                _this.onStateChangedObservable.notifyObservers(_this.state);
+                _this._setState(WebXRState.IN_XR);
             });
             });
         };
         };
         /**
         /**
+         * Checks if the creation options are supported by the xr session
+         * @param options creation options
+         * @returns true if supported
+         */
+        WebXRExperienceHelper.prototype.supportsSession = function (options) {
+            if (!this._supported) {
+                return Promise.resolve(false);
+            }
+            return this._sessionManager.supportsSession(options);
+        };
+        /**
          * Disposes of the experience helper
          * Disposes of the experience helper
          */
          */
         WebXRExperienceHelper.prototype.dispose = function () {
         WebXRExperienceHelper.prototype.dispose = function () {
             this.camera.dispose();
             this.camera.dispose();
             this.container.dispose();
             this.container.dispose();
-            this._removeCanvas();
             this.onStateChangedObservable.clear();
             this.onStateChangedObservable.clear();
             this._sessionManager.dispose();
             this._sessionManager.dispose();
         };
         };
-        // create canvas used to mirror/vr xr content in fullscreen
-        WebXRExperienceHelper.prototype._createCanvas = function () {
+        return WebXRExperienceHelper;
+    }());
+    BABYLON.WebXRExperienceHelper = WebXRExperienceHelper;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.webXRExperienceHelper.js.map
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Options to create the webXR UI
+     */
+    var WebXREnterExitUIOptions = /** @class */ (function () {
+        function WebXREnterExitUIOptions() {
+        }
+        return WebXREnterExitUIOptions;
+    }());
+    BABYLON.WebXREnterExitUIOptions = WebXREnterExitUIOptions;
+    /**
+     * UI to allow the user to enter/exit XR mode
+     */
+    var WebXREnterExitUI = /** @class */ (function () {
+        function WebXREnterExitUI(scene, options) {
+            var _this = this;
+            this.scene = scene;
+            this._buttons = [];
+            this._overlay = document.createElement("div");
+            this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
+            if (options.customButtons) {
+                this._buttons = options.customButtons;
+            }
+            else {
+                var hmdBtn = document.createElement("button");
+                hmdBtn.style.cssText = "color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-repeat:no-repeat; background-position: center; outline: none;";
+                hmdBtn.innerText = "HMD";
+                this._buttons.push({ element: hmdBtn, initializationOptions: { immersive: true } });
+                var windowBtn = document.createElement("button");
+                windowBtn.style.cssText = hmdBtn.style.cssText;
+                windowBtn.innerText = "Window";
+                this._buttons.push({ element: windowBtn, initializationOptions: { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext } });
+            }
+            var renderCanvas = scene.getEngine().getRenderingCanvas();
+            if (renderCanvas && renderCanvas.parentNode) {
+                renderCanvas.parentNode.appendChild(this._overlay);
+                scene.onDisposeObservable.addOnce(function () {
+                    _this.dispose();
+                });
+            }
+        }
+        /**
+         * Creates UI to allow the user to enter/exit XR mode
+         * @param scene the scene to add the ui to
+         * @param helper the xr experience helper to enter/exit xr with
+         * @param options options to configure the UI
+         * @returns the created ui
+         */
+        WebXREnterExitUI.CreateAsync = function (scene, helper, options) {
+            var _this = this;
+            var ui = new WebXREnterExitUI(scene, options);
+            var supportedPromises = ui._buttons.map(function (btn) {
+                return helper.supportsSession(btn.initializationOptions);
+            });
+            return Promise.all(supportedPromises).then(function (results) {
+                results.forEach(function (supported, i) {
+                    if (supported) {
+                        ui._overlay.appendChild(ui._buttons[i].element);
+                        ui._buttons[i].element.onclick = function () { return __awaiter(_this, void 0, void 0, function () {
+                            return __generator(this, function (_a) {
+                                switch (_a.label) {
+                                    case 0:
+                                        if (!(helper.state == BABYLON.WebXRState.IN_XR)) return [3 /*break*/, 2];
+                                        return [4 /*yield*/, helper.exitXR()];
+                                    case 1:
+                                        _a.sent();
+                                        return [2 /*return*/];
+                                    case 2:
+                                        if (!(helper.state == BABYLON.WebXRState.NOT_IN_XR)) return [3 /*break*/, 4];
+                                        return [4 /*yield*/, helper.enterXR(ui._buttons[i].initializationOptions, "eye-level")];
+                                    case 3:
+                                        _a.sent();
+                                        _a.label = 4;
+                                    case 4: return [2 /*return*/];
+                                }
+                            });
+                        }); };
+                    }
+                });
+            });
+        };
+        /**
+         * Disposes of the object
+         */
+        WebXREnterExitUI.prototype.dispose = function () {
+            var renderCanvas = this.scene.getEngine().getRenderingCanvas();
+            if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
+                renderCanvas.parentNode.removeChild(this._overlay);
+            }
+        };
+        return WebXREnterExitUI;
+    }());
+    BABYLON.WebXREnterExitUI = WebXREnterExitUI;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.webXREnterExitUI.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Creates a canvas that is added/removed from the webpage when entering/exiting XR
+     */
+    var WebXRManagedOutputCanvas = /** @class */ (function () {
+        /**
+         * Initializes the canvas to be added/removed upon entering/exiting xr
+         * @param helper the xr experience helper used to trigger adding/removing of the canvas
+         * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
+         */
+        function WebXRManagedOutputCanvas(helper, canvas) {
+            var _this = this;
+            this._canvas = null;
+            /**
+             * xrpresent context of the canvas which can be used to display/mirror xr content
+             */
+            this.canvasContext = null;
+            if (!canvas) {
+                canvas = document.createElement('canvas');
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
+            }
+            this._setManagedOutputCanvas(canvas);
+            helper.onStateChangedObservable.add(function (stateInfo) {
+                if (stateInfo == BABYLON.WebXRState.ENTERING_XR) {
+                    // The canvas is added to the screen before entering XR because currently the xr session must be initialized while the canvas is added render properly
+                    _this._addCanvas();
+                }
+                else if (helper.state == BABYLON.WebXRState.NOT_IN_XR) {
+                    _this._removeCanvas();
+                }
+            });
+        }
+        /**
+         * Disposes of the object
+         */
+        WebXRManagedOutputCanvas.prototype.dispose = function () {
+            this._removeCanvas();
+            this._setManagedOutputCanvas(null);
+        };
+        WebXRManagedOutputCanvas.prototype._setManagedOutputCanvas = function (canvas) {
             this._removeCanvas();
             this._removeCanvas();
-            this._outputCanvas = document.createElement('canvas');
-            this._outputCanvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
-            document.body.appendChild(this._outputCanvas);
-            this._outputCanvasContext = this._outputCanvas.getContext('xrpresent');
+            if (!canvas) {
+                this._canvas = null;
+                this.canvasContext = null;
+            }
+            else {
+                this._canvas = canvas;
+                this.canvasContext = this._canvas.getContext('xrpresent');
+            }
         };
         };
-        WebXRExperienceHelper.prototype._removeCanvas = function () {
-            if (this._outputCanvas && document.body.contains(this._outputCanvas)) {
-                document.body.removeChild(this._outputCanvas);
+        WebXRManagedOutputCanvas.prototype._addCanvas = function () {
+            if (this._canvas) {
+                document.body.appendChild(this._canvas);
             }
             }
         };
         };
-        return WebXRExperienceHelper;
+        WebXRManagedOutputCanvas.prototype._removeCanvas = function () {
+            if (this._canvas && document.body.contains(this._canvas)) {
+                document.body.removeChild(this._canvas);
+            }
+        };
+        return WebXRManagedOutputCanvas;
     }());
     }());
-    BABYLON.WebXRExperienceHelper = WebXRExperienceHelper;
+    BABYLON.WebXRManagedOutputCanvas = WebXRManagedOutputCanvas;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 
-//# sourceMappingURL=babylon.webXRExperienceHelper.js.map
+//# sourceMappingURL=babylon.webXRManagedOutputCanvas.js.map
 
 
 // Mainly based on these 2 articles :
 // Mainly based on these 2 articles :
 // Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
 // Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
@@ -119743,6 +119982,15 @@ var BABYLON;
         if (webVROptions === void 0) { webVROptions = {}; }
         if (webVROptions === void 0) { webVROptions = {}; }
         return new BABYLON.VRExperienceHelper(this, webVROptions);
         return new BABYLON.VRExperienceHelper(this, webVROptions);
     };
     };
+    BABYLON.Scene.prototype.createDefaultXRExperienceAsync = function () {
+        var _this = this;
+        return BABYLON.WebXRExperienceHelper.CreateAsync(this).then(function (helper) {
+            var outputCanvas = new BABYLON.WebXRManagedOutputCanvas(helper);
+            return BABYLON.WebXREnterExitUI.CreateAsync(_this, helper, { outputCanvasContext: outputCanvas.canvasContext }).then(function (ui) {
+                return helper;
+            });
+        });
+    };
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 
 //# sourceMappingURL=babylon.sceneHelpers.js.map
 //# sourceMappingURL=babylon.sceneHelpers.js.map

+ 348 - 100
dist/preview release/babylon.no-module.max.js

@@ -7470,16 +7470,30 @@ var BABYLON;
                 return 1;
                 return 1;
             }
             }
             var m = this._m;
             var m = this._m;
-            var temp1 = m[10] * m[15] - m[11] * m[14];
-            var temp2 = m[9] * m[15] - m[11] * m[13];
-            var temp3 = m[9] * m[14] - m[10] * m[13];
-            var temp4 = m[8] * m[15] - m[11] * m[12];
-            var temp5 = m[8] * m[14] - m[10] * m[12];
-            var temp6 = m[8] * m[13] - m[9] * m[12];
-            return (m[0] * (m[5] * temp1 - m[6] * temp2 + m[7] * temp3) -
-                m[1] * (m[4] * temp1 - m[6] * temp4 + m[7] * temp5) +
-                m[2] * (m[4] * temp2 - m[5] * temp4 + m[7] * temp6) -
-                m[3] * (m[4] * temp3 - m[5] * temp5 + m[6] * temp6));
+            var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
+            var m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
+            var m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
+            var m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
+            // https://en.wikipedia.org/wiki/Laplace_expansion
+            // to compute the deterrminant of a 4x4 Matrix we compute the cofactors of any row or column,
+            // then we multiply each Cofactor by its corresponding matrix value and sum them all to get the determinant
+            // Cofactor(i, j) = sign(i,j) * det(Minor(i, j))
+            // where
+            //  - sign(i,j) = (i+j) % 2 === 0 ? 1 : -1
+            //  - Minor(i, j) is the 3x3 matrix we get by removing row i and column j from current Matrix
+            //
+            // Here we do that for the 1st row.
+            var det_22_33 = m22 * m33 - m32 * m23;
+            var det_21_33 = m21 * m33 - m31 * m23;
+            var det_21_32 = m21 * m32 - m31 * m22;
+            var det_20_33 = m20 * m33 - m30 * m23;
+            var det_20_32 = m20 * m32 - m22 * m30;
+            var det_20_31 = m20 * m31 - m30 * m21;
+            var cofact_00 = +(m11 * det_22_33 - m12 * det_21_33 + m13 * det_21_32);
+            var cofact_01 = -(m10 * det_22_33 - m12 * det_20_33 + m13 * det_20_32);
+            var cofact_02 = +(m10 * det_21_33 - m11 * det_20_33 + m13 * det_20_31);
+            var cofact_03 = -(m10 * det_21_32 - m11 * det_20_32 + m12 * det_20_31);
+            return m00 * cofact_00 + m01 * cofact_01 + m02 * cofact_02 + m03 * cofact_03;
         };
         };
         // Methods
         // Methods
         /**
         /**
@@ -7558,52 +7572,54 @@ var BABYLON;
                 Matrix.IdentityToRef(other);
                 Matrix.IdentityToRef(other);
                 return this;
                 return this;
             }
             }
+            // the inverse of a Matrix is the transpose of cofactor matrix divided by the determinant
             var m = this._m;
             var m = this._m;
-            var l1 = m[0], l2 = m[1], l3 = m[2], l4 = m[3];
-            var l5 = m[4], l6 = m[5], l7 = m[6], l8 = m[7];
-            var l9 = m[8], l10 = m[9], l11 = m[10], l12 = m[11];
-            var l13 = m[12], l14 = m[13], l15 = m[14], l16 = m[15];
-            var l17 = l11 * l16 - l12 * l15;
-            var l18 = l10 * l16 - l12 * l14;
-            var l19 = l10 * l15 - l11 * l14;
-            var l20 = l9 * l16 - l12 * l13;
-            var l21 = l9 * l15 - l11 * l13;
-            var l22 = l9 * l14 - l10 * l13;
-            var l23 = l6 * l17 - l7 * l18 + l8 * l19;
-            var l24 = -(l5 * l17 - l7 * l20 + l8 * l21);
-            var l25 = l5 * l18 - l6 * l20 + l8 * l22;
-            var l26 = -(l5 * l19 - l6 * l21 + l7 * l22);
-            var l27 = 1 / (l1 * l23 + l2 * l24 + l3 * l25 + l4 * l26);
-            var l28 = l7 * l16 - l8 * l15;
-            var l29 = l6 * l16 - l8 * l14;
-            var l30 = l6 * l15 - l7 * l14;
-            var l31 = l5 * l16 - l8 * l13;
-            var l32 = l5 * l15 - l7 * l13;
-            var l33 = l5 * l14 - l6 * l13;
-            var l34 = l7 * l12 - l8 * l11;
-            var l35 = l6 * l12 - l8 * l10;
-            var l36 = l6 * l11 - l7 * l10;
-            var l37 = l5 * l12 - l8 * l9;
-            var l38 = l5 * l11 - l7 * l9;
-            var l39 = l5 * l10 - l6 * l9;
-            var otherM = other._m;
-            otherM[0] = l23 * l27;
-            otherM[4] = l24 * l27;
-            otherM[8] = l25 * l27;
-            otherM[12] = l26 * l27;
-            otherM[1] = -(l2 * l17 - l3 * l18 + l4 * l19) * l27;
-            otherM[5] = (l1 * l17 - l3 * l20 + l4 * l21) * l27;
-            otherM[9] = -(l1 * l18 - l2 * l20 + l4 * l22) * l27;
-            otherM[13] = (l1 * l19 - l2 * l21 + l3 * l22) * l27;
-            otherM[2] = (l2 * l28 - l3 * l29 + l4 * l30) * l27;
-            otherM[6] = -(l1 * l28 - l3 * l31 + l4 * l32) * l27;
-            otherM[10] = (l1 * l29 - l2 * l31 + l4 * l33) * l27;
-            otherM[14] = -(l1 * l30 - l2 * l32 + l3 * l33) * l27;
-            otherM[3] = -(l2 * l34 - l3 * l35 + l4 * l36) * l27;
-            otherM[7] = (l1 * l34 - l3 * l37 + l4 * l38) * l27;
-            otherM[11] = -(l1 * l35 - l2 * l37 + l4 * l39) * l27;
-            otherM[15] = (l1 * l36 - l2 * l38 + l3 * l39) * l27;
-            other._markAsUpdated();
+            var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
+            var m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
+            var m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
+            var m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
+            var det_22_33 = m22 * m33 - m32 * m23;
+            var det_21_33 = m21 * m33 - m31 * m23;
+            var det_21_32 = m21 * m32 - m31 * m22;
+            var det_20_33 = m20 * m33 - m30 * m23;
+            var det_20_32 = m20 * m32 - m22 * m30;
+            var det_20_31 = m20 * m31 - m30 * m21;
+            var cofact_00 = +(m11 * det_22_33 - m12 * det_21_33 + m13 * det_21_32);
+            var cofact_01 = -(m10 * det_22_33 - m12 * det_20_33 + m13 * det_20_32);
+            var cofact_02 = +(m10 * det_21_33 - m11 * det_20_33 + m13 * det_20_31);
+            var cofact_03 = -(m10 * det_21_32 - m11 * det_20_32 + m12 * det_20_31);
+            var det = m00 * cofact_00 + m01 * cofact_01 + m02 * cofact_02 + m03 * cofact_03;
+            if (det === 0) {
+                // not invertible
+                other.copyFrom(this);
+                return this;
+            }
+            var detInv = 1 / det;
+            var det_12_33 = m12 * m33 - m32 * m13;
+            var det_11_33 = m11 * m33 - m31 * m13;
+            var det_11_32 = m11 * m32 - m31 * m12;
+            var det_10_33 = m10 * m33 - m30 * m13;
+            var det_10_32 = m10 * m32 - m30 * m12;
+            var det_10_31 = m10 * m31 - m30 * m11;
+            var det_12_23 = m12 * m23 - m22 * m13;
+            var det_11_23 = m11 * m23 - m21 * m13;
+            var det_11_22 = m11 * m22 - m21 * m12;
+            var det_10_23 = m10 * m23 - m20 * m13;
+            var det_10_22 = m10 * m22 - m20 * m12;
+            var det_10_21 = m10 * m21 - m20 * m11;
+            var cofact_10 = -(m01 * det_22_33 - m02 * det_21_33 + m03 * det_21_32);
+            var cofact_11 = +(m00 * det_22_33 - m02 * det_20_33 + m03 * det_20_32);
+            var cofact_12 = -(m00 * det_21_33 - m01 * det_20_33 + m03 * det_20_31);
+            var cofact_13 = +(m00 * det_21_32 - m01 * det_20_32 + m02 * det_20_31);
+            var cofact_20 = +(m01 * det_12_33 - m02 * det_11_33 + m03 * det_11_32);
+            var cofact_21 = -(m00 * det_12_33 - m02 * det_10_33 + m03 * det_10_32);
+            var cofact_22 = +(m00 * det_11_33 - m01 * det_10_33 + m03 * det_10_31);
+            var cofact_23 = -(m00 * det_11_32 - m01 * det_10_32 + m02 * det_10_31);
+            var cofact_30 = -(m01 * det_12_23 - m02 * det_11_23 + m03 * det_11_22);
+            var cofact_31 = +(m00 * det_12_23 - m02 * det_10_23 + m03 * det_10_22);
+            var cofact_32 = -(m00 * det_11_23 - m01 * det_10_23 + m03 * det_10_21);
+            var cofact_33 = +(m00 * det_11_22 - m01 * det_10_22 + m02 * det_10_21);
+            Matrix.FromValuesToRef(cofact_00 * detInv, cofact_10 * detInv, cofact_20 * detInv, cofact_30 * detInv, cofact_01 * detInv, cofact_11 * detInv, cofact_21 * detInv, cofact_31 * detInv, cofact_02 * detInv, cofact_12 * detInv, cofact_22 * detInv, cofact_32 * detInv, cofact_03 * detInv, cofact_13 * detInv, cofact_23 * detInv, cofact_33 * detInv, other);
             return this;
             return this;
         };
         };
         /**
         /**
@@ -8302,6 +8318,9 @@ var BABYLON;
             m[9] = (axis.z * axis.y) * c1 + (axis.x * s);
             m[9] = (axis.z * axis.y) * c1 + (axis.x * s);
             m[10] = (axis.z * axis.z) * c1 + c;
             m[10] = (axis.z * axis.z) * c1 + c;
             m[11] = 0.0;
             m[11] = 0.0;
+            m[12] = 0.0;
+            m[13] = 0.0;
+            m[14] = 0.0;
             m[15] = 1.0;
             m[15] = 1.0;
             result._markAsUpdated();
             result._markAsUpdated();
         };
         };
@@ -12651,7 +12670,7 @@ var BABYLON;
              * Returns the current version of the framework
              * Returns the current version of the framework
              */
              */
             get: function () {
             get: function () {
-                return "4.0.0-alpha.2";
+                return "4.0.0-alpha.4";
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -12889,7 +12908,7 @@ var BABYLON;
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
-            this._caps.highPrecisionShaderSupported = true;
+            this._caps.highPrecisionShaderSupported = false;
             this._caps.timerQuery = this._gl.getExtension('EXT_disjoint_timer_query_webgl2') || this._gl.getExtension("EXT_disjoint_timer_query");
             this._caps.timerQuery = this._gl.getExtension('EXT_disjoint_timer_query_webgl2') || this._gl.getExtension("EXT_disjoint_timer_query");
             if (this._caps.timerQuery) {
             if (this._caps.timerQuery) {
                 if (this._webGLVersion === 1) {
                 if (this._webGLVersion === 1) {
@@ -12997,9 +13016,10 @@ var BABYLON;
                 this.texturesSupported.push('-etc1.ktx');
                 this.texturesSupported.push('-etc1.ktx');
             }
             }
             if (this._gl.getShaderPrecisionFormat) {
             if (this._gl.getShaderPrecisionFormat) {
-                var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
-                if (highp) {
-                    this._caps.highPrecisionShaderSupported = highp.precision !== 0;
+                var vertex_highp = this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER, this._gl.HIGH_FLOAT);
+                var fragment_highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
+                if (vertex_highp && fragment_highp) {
+                    this._caps.highPrecisionShaderSupported = vertex_highp.precision !== 0 && fragment_highp.precision !== 0;
                 }
                 }
             }
             }
             // Depth buffer
             // Depth buffer
@@ -51785,7 +51805,7 @@ var BABYLON;
         };
         };
         ArcRotateCamera.prototype._getTargetPosition = function () {
         ArcRotateCamera.prototype._getTargetPosition = function () {
             if (this._targetHost && this._targetHost.getAbsolutePosition) {
             if (this._targetHost && this._targetHost.getAbsolutePosition) {
-                var pos = this._targetHost.getAbsolutePosition();
+                var pos = this._targetHost.absolutePosition;
                 if (this._targetBoundingCenter) {
                 if (this._targetBoundingCenter) {
                     pos.addToRef(this._targetBoundingCenter, this._target);
                     pos.addToRef(this._targetBoundingCenter, this._target);
                 }
                 }
@@ -105739,10 +105759,6 @@ var BABYLON;
              */
              */
             this.alphaCorrection = 1;
             this.alphaCorrection = 1;
             /**
             /**
-             * Defines a correction factor applied on the beta value retrieved from the orientation events.
-             */
-            this.betaCorrection = 1;
-            /**
              * Defines a correction factor applied on the gamma value retrieved from the orientation events.
              * Defines a correction factor applied on the gamma value retrieved from the orientation events.
              */
              */
             this.gammaCorrection = 1;
             this.gammaCorrection = 1;
@@ -105763,10 +105779,10 @@ var BABYLON;
         /** @hidden */
         /** @hidden */
         ArcRotateCameraVRDeviceOrientationInput.prototype._onOrientationEvent = function (evt) {
         ArcRotateCameraVRDeviceOrientationInput.prototype._onOrientationEvent = function (evt) {
             if (evt.alpha !== null) {
             if (evt.alpha !== null) {
-                this._alpha = +evt.alpha | 0;
+                this._alpha = (+evt.alpha | 0) * this.alphaCorrection;
             }
             }
             if (evt.gamma !== null) {
             if (evt.gamma !== null) {
-                this._gamma = +evt.gamma | 0;
+                this._gamma = (+evt.gamma | 0) * this.gammaCorrection;
             }
             }
             this._dirty = true;
             this._dirty = true;
         };
         };
@@ -108556,7 +108572,7 @@ var BABYLON;
             this._tmpMatrix = new BABYLON.Matrix();
             this._tmpMatrix = new BABYLON.Matrix();
         }
         }
         /**
         /**
-         * Initializes the manager, this must be done with a user action (eg. button click event)
+         * Initializes the manager
          * After initialization enterXR can be called to start an XR session
          * After initialization enterXR can be called to start an XR session
          * @returns Promise which resolves after it is initialized
          * @returns Promise which resolves after it is initialized
          */
          */
@@ -108574,7 +108590,7 @@ var BABYLON;
             });
             });
         };
         };
         /**
         /**
-         * Enters XR with the desired XR session options
+         * Enters XR with the desired XR session options, this must be done with a user action (eg. button click event)
          * @param sessionCreationOptions xr options to create the session with
          * @param sessionCreationOptions xr options to create the session with
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @returns Promise which resolves after it enters XR
          * @returns Promise which resolves after it enters XR
@@ -108658,6 +108674,18 @@ var BABYLON;
             });
             });
         };
         };
         /**
         /**
+         * Checks if a session would be supported for the creation options specified
+         * @param options creation options to check if they are supported
+         * @returns true if supported
+         */
+        WebXRSessionManager.prototype.supportsSession = function (options) {
+            return this._xrDevice.supportsSession(options).then(function () {
+                return true;
+            }).catch(function (e) {
+                return false;
+            });
+        };
+        /**
          * @hidden
          * @hidden
          * Converts the render layer of xrSession to a render target
          * Converts the render layer of xrSession to a render target
          * @param session session to create render target for
          * @param session session to create render target for
@@ -108696,17 +108724,21 @@ var BABYLON;
     var WebXRState;
     var WebXRState;
     (function (WebXRState) {
     (function (WebXRState) {
         /**
         /**
-         * Transitioning to/from being in XR mode
+         * Transitioning to being in XR mode
+         */
+        WebXRState[WebXRState["ENTERING_XR"] = 0] = "ENTERING_XR";
+        /**
+         * Transitioning to non XR mode
          */
          */
-        WebXRState[WebXRState["TRANSITION"] = 0] = "TRANSITION";
+        WebXRState[WebXRState["EXITING_XR"] = 1] = "EXITING_XR";
         /**
         /**
          * In XR mode and presenting
          * In XR mode and presenting
          */
          */
-        WebXRState[WebXRState["IN_XR"] = 1] = "IN_XR";
+        WebXRState[WebXRState["IN_XR"] = 2] = "IN_XR";
         /**
         /**
          * Not entered XR mode
          * Not entered XR mode
          */
          */
-        WebXRState[WebXRState["NOT_IN_XR"] = 2] = "NOT_IN_XR";
+        WebXRState[WebXRState["NOT_IN_XR"] = 3] = "NOT_IN_XR";
     })(WebXRState = BABYLON.WebXRState || (BABYLON.WebXRState = {}));
     })(WebXRState = BABYLON.WebXRState || (BABYLON.WebXRState = {}));
     /**
     /**
      * Helper class used to enable XR
      * Helper class used to enable XR
@@ -108729,18 +108761,35 @@ var BABYLON;
             this.onStateChangedObservable = new BABYLON.Observable();
             this.onStateChangedObservable = new BABYLON.Observable();
             this._nonVRCamera = null;
             this._nonVRCamera = null;
             this._originalSceneAutoClear = true;
             this._originalSceneAutoClear = true;
+            this._supported = false;
             this.camera = new BABYLON.WebXRCamera("", scene);
             this.camera = new BABYLON.WebXRCamera("", scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this.container = new BABYLON.AbstractMesh("", scene);
             this.container = new BABYLON.AbstractMesh("", scene);
-            this._sessionManager.initialize();
         }
         }
+        WebXRExperienceHelper.prototype._setState = function (val) {
+            this.state = val;
+            this.onStateChangedObservable.notifyObservers(this.state);
+        };
+        /**
+         * Creates the experience helper
+         * @param scene the scene to attach the experience helper to
+         * @returns a promise for the experience helper
+         */
+        WebXRExperienceHelper.CreateAsync = function (scene) {
+            var helper = new WebXRExperienceHelper(scene);
+            return helper._sessionManager.initialize().then(function () {
+                helper._supported = true;
+                return helper;
+            }).catch(function () {
+                return helper;
+            });
+        };
         /**
         /**
          * Exits XR mode and returns the scene to its original state
          * Exits XR mode and returns the scene to its original state
          * @returns promise that resolves after xr mode has exited
          * @returns promise that resolves after xr mode has exited
          */
          */
         WebXRExperienceHelper.prototype.exitXR = function () {
         WebXRExperienceHelper.prototype.exitXR = function () {
-            this.state = WebXRState.TRANSITION;
-            this.onStateChangedObservable.notifyObservers(this.state);
+            this._setState(WebXRState.EXITING_XR);
             return this._sessionManager.exitXR();
             return this._sessionManager.exitXR();
         };
         };
         /**
         /**
@@ -108751,12 +108800,7 @@ var BABYLON;
          */
          */
         WebXRExperienceHelper.prototype.enterXR = function (sessionCreationOptions, frameOfReference) {
         WebXRExperienceHelper.prototype.enterXR = function (sessionCreationOptions, frameOfReference) {
             var _this = this;
             var _this = this;
-            this.state = WebXRState.TRANSITION;
-            this.onStateChangedObservable.notifyObservers(this.state);
-            this._createCanvas();
-            if (!sessionCreationOptions.outputContext) {
-                sessionCreationOptions.outputContext = this._outputCanvasContext;
-            }
+            this._setState(WebXRState.ENTERING_XR);
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(function () {
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(function () {
                 // Cache pre xr scene settings
                 // Cache pre xr scene settings
                 _this._originalSceneAutoClear = _this.scene.autoClear;
                 _this._originalSceneAutoClear = _this.scene.autoClear;
@@ -108776,43 +108820,238 @@ var BABYLON;
                     _this.scene.autoClear = _this._originalSceneAutoClear;
                     _this.scene.autoClear = _this._originalSceneAutoClear;
                     _this.scene.activeCamera = _this._nonVRCamera;
                     _this.scene.activeCamera = _this._nonVRCamera;
                     _this._sessionManager.onXRFrameObservable.clear();
                     _this._sessionManager.onXRFrameObservable.clear();
-                    _this._removeCanvas();
-                    _this.state = WebXRState.NOT_IN_XR;
-                    _this.onStateChangedObservable.notifyObservers(_this.state);
+                    _this._setState(WebXRState.NOT_IN_XR);
                 });
                 });
-                _this.state = WebXRState.IN_XR;
-                _this.onStateChangedObservable.notifyObservers(_this.state);
+                _this._setState(WebXRState.IN_XR);
             });
             });
         };
         };
         /**
         /**
+         * Checks if the creation options are supported by the xr session
+         * @param options creation options
+         * @returns true if supported
+         */
+        WebXRExperienceHelper.prototype.supportsSession = function (options) {
+            if (!this._supported) {
+                return Promise.resolve(false);
+            }
+            return this._sessionManager.supportsSession(options);
+        };
+        /**
          * Disposes of the experience helper
          * Disposes of the experience helper
          */
          */
         WebXRExperienceHelper.prototype.dispose = function () {
         WebXRExperienceHelper.prototype.dispose = function () {
             this.camera.dispose();
             this.camera.dispose();
             this.container.dispose();
             this.container.dispose();
-            this._removeCanvas();
             this.onStateChangedObservable.clear();
             this.onStateChangedObservable.clear();
             this._sessionManager.dispose();
             this._sessionManager.dispose();
         };
         };
-        // create canvas used to mirror/vr xr content in fullscreen
-        WebXRExperienceHelper.prototype._createCanvas = function () {
+        return WebXRExperienceHelper;
+    }());
+    BABYLON.WebXRExperienceHelper = WebXRExperienceHelper;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.webXRExperienceHelper.js.map
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Options to create the webXR UI
+     */
+    var WebXREnterExitUIOptions = /** @class */ (function () {
+        function WebXREnterExitUIOptions() {
+        }
+        return WebXREnterExitUIOptions;
+    }());
+    BABYLON.WebXREnterExitUIOptions = WebXREnterExitUIOptions;
+    /**
+     * UI to allow the user to enter/exit XR mode
+     */
+    var WebXREnterExitUI = /** @class */ (function () {
+        function WebXREnterExitUI(scene, options) {
+            var _this = this;
+            this.scene = scene;
+            this._buttons = [];
+            this._overlay = document.createElement("div");
+            this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
+            if (options.customButtons) {
+                this._buttons = options.customButtons;
+            }
+            else {
+                var hmdBtn = document.createElement("button");
+                hmdBtn.style.cssText = "color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-repeat:no-repeat; background-position: center; outline: none;";
+                hmdBtn.innerText = "HMD";
+                this._buttons.push({ element: hmdBtn, initializationOptions: { immersive: true } });
+                var windowBtn = document.createElement("button");
+                windowBtn.style.cssText = hmdBtn.style.cssText;
+                windowBtn.innerText = "Window";
+                this._buttons.push({ element: windowBtn, initializationOptions: { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext } });
+            }
+            var renderCanvas = scene.getEngine().getRenderingCanvas();
+            if (renderCanvas && renderCanvas.parentNode) {
+                renderCanvas.parentNode.appendChild(this._overlay);
+                scene.onDisposeObservable.addOnce(function () {
+                    _this.dispose();
+                });
+            }
+        }
+        /**
+         * Creates UI to allow the user to enter/exit XR mode
+         * @param scene the scene to add the ui to
+         * @param helper the xr experience helper to enter/exit xr with
+         * @param options options to configure the UI
+         * @returns the created ui
+         */
+        WebXREnterExitUI.CreateAsync = function (scene, helper, options) {
+            var _this = this;
+            var ui = new WebXREnterExitUI(scene, options);
+            var supportedPromises = ui._buttons.map(function (btn) {
+                return helper.supportsSession(btn.initializationOptions);
+            });
+            return Promise.all(supportedPromises).then(function (results) {
+                results.forEach(function (supported, i) {
+                    if (supported) {
+                        ui._overlay.appendChild(ui._buttons[i].element);
+                        ui._buttons[i].element.onclick = function () { return __awaiter(_this, void 0, void 0, function () {
+                            return __generator(this, function (_a) {
+                                switch (_a.label) {
+                                    case 0:
+                                        if (!(helper.state == BABYLON.WebXRState.IN_XR)) return [3 /*break*/, 2];
+                                        return [4 /*yield*/, helper.exitXR()];
+                                    case 1:
+                                        _a.sent();
+                                        return [2 /*return*/];
+                                    case 2:
+                                        if (!(helper.state == BABYLON.WebXRState.NOT_IN_XR)) return [3 /*break*/, 4];
+                                        return [4 /*yield*/, helper.enterXR(ui._buttons[i].initializationOptions, "eye-level")];
+                                    case 3:
+                                        _a.sent();
+                                        _a.label = 4;
+                                    case 4: return [2 /*return*/];
+                                }
+                            });
+                        }); };
+                    }
+                });
+            });
+        };
+        /**
+         * Disposes of the object
+         */
+        WebXREnterExitUI.prototype.dispose = function () {
+            var renderCanvas = this.scene.getEngine().getRenderingCanvas();
+            if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
+                renderCanvas.parentNode.removeChild(this._overlay);
+            }
+        };
+        return WebXREnterExitUI;
+    }());
+    BABYLON.WebXREnterExitUI = WebXREnterExitUI;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.webXREnterExitUI.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Creates a canvas that is added/removed from the webpage when entering/exiting XR
+     */
+    var WebXRManagedOutputCanvas = /** @class */ (function () {
+        /**
+         * Initializes the canvas to be added/removed upon entering/exiting xr
+         * @param helper the xr experience helper used to trigger adding/removing of the canvas
+         * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
+         */
+        function WebXRManagedOutputCanvas(helper, canvas) {
+            var _this = this;
+            this._canvas = null;
+            /**
+             * xrpresent context of the canvas which can be used to display/mirror xr content
+             */
+            this.canvasContext = null;
+            if (!canvas) {
+                canvas = document.createElement('canvas');
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
+            }
+            this._setManagedOutputCanvas(canvas);
+            helper.onStateChangedObservable.add(function (stateInfo) {
+                if (stateInfo == BABYLON.WebXRState.ENTERING_XR) {
+                    // The canvas is added to the screen before entering XR because currently the xr session must be initialized while the canvas is added render properly
+                    _this._addCanvas();
+                }
+                else if (helper.state == BABYLON.WebXRState.NOT_IN_XR) {
+                    _this._removeCanvas();
+                }
+            });
+        }
+        /**
+         * Disposes of the object
+         */
+        WebXRManagedOutputCanvas.prototype.dispose = function () {
+            this._removeCanvas();
+            this._setManagedOutputCanvas(null);
+        };
+        WebXRManagedOutputCanvas.prototype._setManagedOutputCanvas = function (canvas) {
             this._removeCanvas();
             this._removeCanvas();
-            this._outputCanvas = document.createElement('canvas');
-            this._outputCanvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
-            document.body.appendChild(this._outputCanvas);
-            this._outputCanvasContext = this._outputCanvas.getContext('xrpresent');
+            if (!canvas) {
+                this._canvas = null;
+                this.canvasContext = null;
+            }
+            else {
+                this._canvas = canvas;
+                this.canvasContext = this._canvas.getContext('xrpresent');
+            }
         };
         };
-        WebXRExperienceHelper.prototype._removeCanvas = function () {
-            if (this._outputCanvas && document.body.contains(this._outputCanvas)) {
-                document.body.removeChild(this._outputCanvas);
+        WebXRManagedOutputCanvas.prototype._addCanvas = function () {
+            if (this._canvas) {
+                document.body.appendChild(this._canvas);
             }
             }
         };
         };
-        return WebXRExperienceHelper;
+        WebXRManagedOutputCanvas.prototype._removeCanvas = function () {
+            if (this._canvas && document.body.contains(this._canvas)) {
+                document.body.removeChild(this._canvas);
+            }
+        };
+        return WebXRManagedOutputCanvas;
     }());
     }());
-    BABYLON.WebXRExperienceHelper = WebXRExperienceHelper;
+    BABYLON.WebXRManagedOutputCanvas = WebXRManagedOutputCanvas;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 
-//# sourceMappingURL=babylon.webXRExperienceHelper.js.map
+//# sourceMappingURL=babylon.webXRManagedOutputCanvas.js.map
 
 
 // Mainly based on these 2 articles :
 // Mainly based on these 2 articles :
 // Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
 // Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
@@ -119710,6 +119949,15 @@ var BABYLON;
         if (webVROptions === void 0) { webVROptions = {}; }
         if (webVROptions === void 0) { webVROptions = {}; }
         return new BABYLON.VRExperienceHelper(this, webVROptions);
         return new BABYLON.VRExperienceHelper(this, webVROptions);
     };
     };
+    BABYLON.Scene.prototype.createDefaultXRExperienceAsync = function () {
+        var _this = this;
+        return BABYLON.WebXRExperienceHelper.CreateAsync(this).then(function (helper) {
+            var outputCanvas = new BABYLON.WebXRManagedOutputCanvas(helper);
+            return BABYLON.WebXREnterExitUI.CreateAsync(_this, helper, { outputCanvasContext: outputCanvas.canvasContext }).then(function (ui) {
+                return helper;
+            });
+        });
+    };
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 
 //# sourceMappingURL=babylon.sceneHelpers.js.map
 //# sourceMappingURL=babylon.sceneHelpers.js.map

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


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


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

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

+ 1 - 1
dist/preview release/gui/babylon.gui.js

@@ -4936,7 +4936,7 @@ var ImageBasedSlider = /** @class */ (function (_super) {
                     }
                     }
                 }
                 }
                 else {
                 else {
-                    this._tempMeasure.copyFromFloats(left, top, thumbPosition, height);
+                    this._tempMeasure.copyFromFloats(left, top, thumbPosition + this._effectiveThumbThickness / 2, height);
                 }
                 }
                 this._valueBarImage._draw(this._tempMeasure, context);
                 this._valueBarImage._draw(this._tempMeasure, context);
             }
             }

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


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


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


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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-gui",
     "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.",
     "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": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.2"
+        "babylonjs": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-inspector",
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.2",
-        "babylonjs-gui": "4.0.0-alpha.2",
-        "babylonjs-loaders": "4.0.0-alpha.2",
-        "babylonjs-serializers": "4.0.0-alpha.2"
+        "babylonjs": "4.0.0-alpha.4",
+        "babylonjs-gui": "4.0.0-alpha.4",
+        "babylonjs-loaders": "4.0.0-alpha.4",
+        "babylonjs-serializers": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-loaders",
     "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.",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.2",
-        "babylonjs": "4.0.0-alpha.2"
+        "babylonjs-gltf2interface": "4.0.0-alpha.4",
+        "babylonjs": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-materials",
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.2"
+        "babylonjs": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-post-process",
     "name": "babylonjs-post-process",
     "description": "The Babylon.js post process library is a collection of advanced post process to be used in a Babylon.js scene.",
     "description": "The Babylon.js post process library is a collection of advanced post process to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.2"
+        "babylonjs": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-procedural-textures",
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.2"
+        "babylonjs": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 128 - 0
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -162,6 +162,122 @@ __export(__webpack_require__(/*! ../src/glTF/2.0 */ "./src/glTF/2.0/index.ts"));
 
 
 /***/ }),
 /***/ }),
 
 
+/***/ "./src/glTF/2.0/Extensions/KHR_texture_transform.ts":
+/*!**********************************************************!*\
+  !*** ./src/glTF/2.0/Extensions/KHR_texture_transform.ts ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", { value: true });
+var babylonjs_1 = __webpack_require__(/*! babylonjs */ "babylonjs");
+var glTFExporter_1 = __webpack_require__(/*! ../glTFExporter */ "./src/glTF/2.0/glTFExporter.ts");
+var NAME = "KHR_texture_transform";
+babylonjs_1.Effect.ShadersStore["textureTransformPixelShader"] = __webpack_require__(/*! ../shaders/textureTransform.fragment.fx */ "./src/glTF/2.0/shaders/textureTransform.fragment.fx");
+/**
+ * @hidden
+ */
+var KHR_texture_transform = /** @class */ (function () {
+    function KHR_texture_transform(exporter) {
+        /** Name of this extension */
+        this.name = NAME;
+        /** Defines whether this extension is enabled */
+        this.enabled = true;
+        /** Defines whether this extension is required */
+        this.required = false;
+        this._exporter = exporter;
+    }
+    KHR_texture_transform.prototype.dispose = function () {
+        delete this._exporter;
+    };
+    KHR_texture_transform.prototype.preExportTextureAsync = function (context, babylonTexture, mimeType) {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            var texture_transform_extension = {};
+            if (babylonTexture.uOffset !== 0 || babylonTexture.vOffset !== 0) {
+                texture_transform_extension.offset = [babylonTexture.uOffset, babylonTexture.vOffset];
+            }
+            if (babylonTexture.uScale !== 1 || babylonTexture.vScale !== 1) {
+                texture_transform_extension.scale = [babylonTexture.uScale, babylonTexture.vScale];
+            }
+            if (babylonTexture.wAng !== 0) {
+                texture_transform_extension.rotation = babylonTexture.wAng;
+            }
+            if (!Object.keys(texture_transform_extension).length) {
+                resolve(babylonTexture);
+            }
+            var scale = texture_transform_extension.scale ? new babylonjs_1.Vector2(texture_transform_extension.scale[0], texture_transform_extension.scale[1]) : babylonjs_1.Vector2.One();
+            var rotation = texture_transform_extension.rotation != null ? texture_transform_extension.rotation : 0;
+            var offset = texture_transform_extension.offset ? new babylonjs_1.Vector2(texture_transform_extension.offset[0], texture_transform_extension.offset[1]) : babylonjs_1.Vector2.Zero();
+            var scene = babylonTexture.getScene();
+            if (!scene) {
+                reject(context + ": \"scene\" is not defined for Babylon texture " + babylonTexture.name + "!");
+            }
+            else {
+                _this.textureTransformTextureAsync(babylonTexture, offset, rotation, scale, scene).then(function (texture) {
+                    resolve(texture);
+                });
+            }
+        });
+    };
+    /**
+     * Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
+     * @param babylonTexture
+     * @param offset
+     * @param rotation
+     * @param scale
+     * @param scene
+     */
+    KHR_texture_transform.prototype.textureTransformTextureAsync = function (babylonTexture, offset, rotation, scale, scene) {
+        return new Promise(function (resolve, reject) {
+            var proceduralTexture = new babylonjs_1.ProceduralTexture("" + babylonTexture.name, babylonTexture.getSize(), "textureTransform", scene);
+            if (!proceduralTexture) {
+                babylonjs_1.Tools.Log("Cannot create procedural texture for " + babylonTexture.name + "!");
+                resolve(babylonTexture);
+            }
+            proceduralTexture.setTexture("textureSampler", babylonTexture);
+            proceduralTexture.setMatrix("textureTransformMat", babylonTexture.getTextureMatrix());
+            // isReady trigger creation of effect if it doesnt exist yet
+            if (proceduralTexture.isReady()) {
+                proceduralTexture.render();
+                resolve(proceduralTexture);
+            }
+            else {
+                proceduralTexture.getEffect().executeWhenCompiled(function () {
+                    proceduralTexture.render();
+                    resolve(proceduralTexture);
+                });
+            }
+        });
+    };
+    return KHR_texture_transform;
+}());
+exports.KHR_texture_transform = KHR_texture_transform;
+glTFExporter_1._Exporter.RegisterExtension(NAME, function (exporter) { return new KHR_texture_transform(exporter); });
+
+
+/***/ }),
+
+/***/ "./src/glTF/2.0/Extensions/index.ts":
+/*!******************************************!*\
+  !*** ./src/glTF/2.0/Extensions/index.ts ***!
+  \******************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+function __export(m) {
+    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}
+Object.defineProperty(exports, "__esModule", { value: true });
+__export(__webpack_require__(/*! ./KHR_texture_transform */ "./src/glTF/2.0/Extensions/KHR_texture_transform.ts"));
+
+
+/***/ }),
+
 /***/ "./src/glTF/2.0/glTFAnimation.ts":
 /***/ "./src/glTF/2.0/glTFAnimation.ts":
 /*!***************************************!*\
 /*!***************************************!*\
   !*** ./src/glTF/2.0/glTFAnimation.ts ***!
   !*** ./src/glTF/2.0/glTFAnimation.ts ***!
@@ -3669,7 +3785,19 @@ __export(__webpack_require__(/*! ./glTFExporterExtension */ "./src/glTF/2.0/glTF
 __export(__webpack_require__(/*! ./glTFMaterialExporter */ "./src/glTF/2.0/glTFMaterialExporter.ts"));
 __export(__webpack_require__(/*! ./glTFMaterialExporter */ "./src/glTF/2.0/glTFMaterialExporter.ts"));
 __export(__webpack_require__(/*! ./glTFSerializer */ "./src/glTF/2.0/glTFSerializer.ts"));
 __export(__webpack_require__(/*! ./glTFSerializer */ "./src/glTF/2.0/glTFSerializer.ts"));
 __export(__webpack_require__(/*! ./glTFUtilities */ "./src/glTF/2.0/glTFUtilities.ts"));
 __export(__webpack_require__(/*! ./glTFUtilities */ "./src/glTF/2.0/glTFUtilities.ts"));
+__export(__webpack_require__(/*! ./Extensions */ "./src/glTF/2.0/Extensions/index.ts"));
+
+
+/***/ }),
+
+/***/ "./src/glTF/2.0/shaders/textureTransform.fragment.fx":
+/*!***********************************************************!*\
+  !*** ./src/glTF/2.0/shaders/textureTransform.fragment.fx ***!
+  \***********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
 
 
+module.exports = "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D textureSampler;\nuniform mat4 textureTransformMat;\nvoid main(void) {\nvec2 uvTransformed=(textureTransformMat*vec4(vUV.xy,1,1)).xy;\ngl_FragColor=texture2D(textureSampler,uvTransformed);\n}"
 
 
 /***/ }),
 /***/ }),
 
 

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js.map


+ 128 - 0
dist/preview release/serializers/babylon.glTF2Serializer.min.js

@@ -162,6 +162,122 @@ __export(__webpack_require__(/*! ../src/glTF/2.0 */ "./src/glTF/2.0/index.ts"));
 
 
 /***/ }),
 /***/ }),
 
 
+/***/ "./src/glTF/2.0/Extensions/KHR_texture_transform.ts":
+/*!**********************************************************!*\
+  !*** ./src/glTF/2.0/Extensions/KHR_texture_transform.ts ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", { value: true });
+var babylonjs_1 = __webpack_require__(/*! babylonjs */ "babylonjs");
+var glTFExporter_1 = __webpack_require__(/*! ../glTFExporter */ "./src/glTF/2.0/glTFExporter.ts");
+var NAME = "KHR_texture_transform";
+babylonjs_1.Effect.ShadersStore["textureTransformPixelShader"] = __webpack_require__(/*! ../shaders/textureTransform.fragment.fx */ "./src/glTF/2.0/shaders/textureTransform.fragment.fx");
+/**
+ * @hidden
+ */
+var KHR_texture_transform = /** @class */ (function () {
+    function KHR_texture_transform(exporter) {
+        /** Name of this extension */
+        this.name = NAME;
+        /** Defines whether this extension is enabled */
+        this.enabled = true;
+        /** Defines whether this extension is required */
+        this.required = false;
+        this._exporter = exporter;
+    }
+    KHR_texture_transform.prototype.dispose = function () {
+        delete this._exporter;
+    };
+    KHR_texture_transform.prototype.preExportTextureAsync = function (context, babylonTexture, mimeType) {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            var texture_transform_extension = {};
+            if (babylonTexture.uOffset !== 0 || babylonTexture.vOffset !== 0) {
+                texture_transform_extension.offset = [babylonTexture.uOffset, babylonTexture.vOffset];
+            }
+            if (babylonTexture.uScale !== 1 || babylonTexture.vScale !== 1) {
+                texture_transform_extension.scale = [babylonTexture.uScale, babylonTexture.vScale];
+            }
+            if (babylonTexture.wAng !== 0) {
+                texture_transform_extension.rotation = babylonTexture.wAng;
+            }
+            if (!Object.keys(texture_transform_extension).length) {
+                resolve(babylonTexture);
+            }
+            var scale = texture_transform_extension.scale ? new babylonjs_1.Vector2(texture_transform_extension.scale[0], texture_transform_extension.scale[1]) : babylonjs_1.Vector2.One();
+            var rotation = texture_transform_extension.rotation != null ? texture_transform_extension.rotation : 0;
+            var offset = texture_transform_extension.offset ? new babylonjs_1.Vector2(texture_transform_extension.offset[0], texture_transform_extension.offset[1]) : babylonjs_1.Vector2.Zero();
+            var scene = babylonTexture.getScene();
+            if (!scene) {
+                reject(context + ": \"scene\" is not defined for Babylon texture " + babylonTexture.name + "!");
+            }
+            else {
+                _this.textureTransformTextureAsync(babylonTexture, offset, rotation, scale, scene).then(function (texture) {
+                    resolve(texture);
+                });
+            }
+        });
+    };
+    /**
+     * Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
+     * @param babylonTexture
+     * @param offset
+     * @param rotation
+     * @param scale
+     * @param scene
+     */
+    KHR_texture_transform.prototype.textureTransformTextureAsync = function (babylonTexture, offset, rotation, scale, scene) {
+        return new Promise(function (resolve, reject) {
+            var proceduralTexture = new babylonjs_1.ProceduralTexture("" + babylonTexture.name, babylonTexture.getSize(), "textureTransform", scene);
+            if (!proceduralTexture) {
+                babylonjs_1.Tools.Log("Cannot create procedural texture for " + babylonTexture.name + "!");
+                resolve(babylonTexture);
+            }
+            proceduralTexture.setTexture("textureSampler", babylonTexture);
+            proceduralTexture.setMatrix("textureTransformMat", babylonTexture.getTextureMatrix());
+            // isReady trigger creation of effect if it doesnt exist yet
+            if (proceduralTexture.isReady()) {
+                proceduralTexture.render();
+                resolve(proceduralTexture);
+            }
+            else {
+                proceduralTexture.getEffect().executeWhenCompiled(function () {
+                    proceduralTexture.render();
+                    resolve(proceduralTexture);
+                });
+            }
+        });
+    };
+    return KHR_texture_transform;
+}());
+exports.KHR_texture_transform = KHR_texture_transform;
+glTFExporter_1._Exporter.RegisterExtension(NAME, function (exporter) { return new KHR_texture_transform(exporter); });
+
+
+/***/ }),
+
+/***/ "./src/glTF/2.0/Extensions/index.ts":
+/*!******************************************!*\
+  !*** ./src/glTF/2.0/Extensions/index.ts ***!
+  \******************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+function __export(m) {
+    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}
+Object.defineProperty(exports, "__esModule", { value: true });
+__export(__webpack_require__(/*! ./KHR_texture_transform */ "./src/glTF/2.0/Extensions/KHR_texture_transform.ts"));
+
+
+/***/ }),
+
 /***/ "./src/glTF/2.0/glTFAnimation.ts":
 /***/ "./src/glTF/2.0/glTFAnimation.ts":
 /*!***************************************!*\
 /*!***************************************!*\
   !*** ./src/glTF/2.0/glTFAnimation.ts ***!
   !*** ./src/glTF/2.0/glTFAnimation.ts ***!
@@ -3669,7 +3785,19 @@ __export(__webpack_require__(/*! ./glTFExporterExtension */ "./src/glTF/2.0/glTF
 __export(__webpack_require__(/*! ./glTFMaterialExporter */ "./src/glTF/2.0/glTFMaterialExporter.ts"));
 __export(__webpack_require__(/*! ./glTFMaterialExporter */ "./src/glTF/2.0/glTFMaterialExporter.ts"));
 __export(__webpack_require__(/*! ./glTFSerializer */ "./src/glTF/2.0/glTFSerializer.ts"));
 __export(__webpack_require__(/*! ./glTFSerializer */ "./src/glTF/2.0/glTFSerializer.ts"));
 __export(__webpack_require__(/*! ./glTFUtilities */ "./src/glTF/2.0/glTFUtilities.ts"));
 __export(__webpack_require__(/*! ./glTFUtilities */ "./src/glTF/2.0/glTFUtilities.ts"));
+__export(__webpack_require__(/*! ./Extensions */ "./src/glTF/2.0/Extensions/index.ts"));
+
+
+/***/ }),
+
+/***/ "./src/glTF/2.0/shaders/textureTransform.fragment.fx":
+/*!***********************************************************!*\
+  !*** ./src/glTF/2.0/shaders/textureTransform.fragment.fx ***!
+  \***********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
 
 
+module.exports = "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D textureSampler;\nuniform mat4 textureTransformMat;\nvoid main(void) {\nvec2 uvTransformed=(textureTransformMat*vec4(vUV.xy,1,1)).xy;\ngl_FragColor=texture2D(textureSampler,uvTransformed);\n}"
 
 
 /***/ }),
 /***/ }),
 
 

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js.map


+ 25 - 0
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -557,4 +557,29 @@ declare module BABYLON {
             static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
             static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
             static _NormalizeTangentFromRef(tangent: BABYLON.Vector4): void;
             static _NormalizeTangentFromRef(tangent: BABYLON.Vector4): void;
     }
     }
+}
+declare module BABYLON {
+    /**
+        * @hidden
+        */
+    export class KHR_texture_transform implements IGLTFExporterExtensionV2 {
+            /** Name of this extension */
+            readonly name: string;
+            /** Defines whether this extension is enabled */
+            enabled: boolean;
+            /** Defines whether this extension is required */
+            required: boolean;
+            constructor(exporter: _Exporter);
+            dispose(): void;
+            preExportTextureAsync(context: string, babylonTexture: BABYLON.Texture, mimeType: BABYLON.GLTF2.ImageMimeType): BABYLON.Nullable<Promise<BABYLON.Texture>>;
+            /**
+                * Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
+                * @param babylonTexture
+                * @param offset
+                * @param rotation
+                * @param scale
+                * @param scene
+                */
+            textureTransformTextureAsync(babylonTexture: BABYLON.Texture, offset: BABYLON.Vector2, rotation: number, scale: BABYLON.Vector2, scene: BABYLON.Scene): Promise<BABYLON.BaseTexture>;
+    }
 }
 }

+ 128 - 0
dist/preview release/serializers/babylonjs.serializers.js

@@ -303,6 +303,122 @@ exports.OBJExport = OBJExport;
 
 
 /***/ }),
 /***/ }),
 
 
+/***/ "./src/glTF/2.0/Extensions/KHR_texture_transform.ts":
+/*!**********************************************************!*\
+  !*** ./src/glTF/2.0/Extensions/KHR_texture_transform.ts ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", { value: true });
+var babylonjs_1 = __webpack_require__(/*! babylonjs */ "babylonjs");
+var glTFExporter_1 = __webpack_require__(/*! ../glTFExporter */ "./src/glTF/2.0/glTFExporter.ts");
+var NAME = "KHR_texture_transform";
+babylonjs_1.Effect.ShadersStore["textureTransformPixelShader"] = __webpack_require__(/*! ../shaders/textureTransform.fragment.fx */ "./src/glTF/2.0/shaders/textureTransform.fragment.fx");
+/**
+ * @hidden
+ */
+var KHR_texture_transform = /** @class */ (function () {
+    function KHR_texture_transform(exporter) {
+        /** Name of this extension */
+        this.name = NAME;
+        /** Defines whether this extension is enabled */
+        this.enabled = true;
+        /** Defines whether this extension is required */
+        this.required = false;
+        this._exporter = exporter;
+    }
+    KHR_texture_transform.prototype.dispose = function () {
+        delete this._exporter;
+    };
+    KHR_texture_transform.prototype.preExportTextureAsync = function (context, babylonTexture, mimeType) {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            var texture_transform_extension = {};
+            if (babylonTexture.uOffset !== 0 || babylonTexture.vOffset !== 0) {
+                texture_transform_extension.offset = [babylonTexture.uOffset, babylonTexture.vOffset];
+            }
+            if (babylonTexture.uScale !== 1 || babylonTexture.vScale !== 1) {
+                texture_transform_extension.scale = [babylonTexture.uScale, babylonTexture.vScale];
+            }
+            if (babylonTexture.wAng !== 0) {
+                texture_transform_extension.rotation = babylonTexture.wAng;
+            }
+            if (!Object.keys(texture_transform_extension).length) {
+                resolve(babylonTexture);
+            }
+            var scale = texture_transform_extension.scale ? new babylonjs_1.Vector2(texture_transform_extension.scale[0], texture_transform_extension.scale[1]) : babylonjs_1.Vector2.One();
+            var rotation = texture_transform_extension.rotation != null ? texture_transform_extension.rotation : 0;
+            var offset = texture_transform_extension.offset ? new babylonjs_1.Vector2(texture_transform_extension.offset[0], texture_transform_extension.offset[1]) : babylonjs_1.Vector2.Zero();
+            var scene = babylonTexture.getScene();
+            if (!scene) {
+                reject(context + ": \"scene\" is not defined for Babylon texture " + babylonTexture.name + "!");
+            }
+            else {
+                _this.textureTransformTextureAsync(babylonTexture, offset, rotation, scale, scene).then(function (texture) {
+                    resolve(texture);
+                });
+            }
+        });
+    };
+    /**
+     * Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
+     * @param babylonTexture
+     * @param offset
+     * @param rotation
+     * @param scale
+     * @param scene
+     */
+    KHR_texture_transform.prototype.textureTransformTextureAsync = function (babylonTexture, offset, rotation, scale, scene) {
+        return new Promise(function (resolve, reject) {
+            var proceduralTexture = new babylonjs_1.ProceduralTexture("" + babylonTexture.name, babylonTexture.getSize(), "textureTransform", scene);
+            if (!proceduralTexture) {
+                babylonjs_1.Tools.Log("Cannot create procedural texture for " + babylonTexture.name + "!");
+                resolve(babylonTexture);
+            }
+            proceduralTexture.setTexture("textureSampler", babylonTexture);
+            proceduralTexture.setMatrix("textureTransformMat", babylonTexture.getTextureMatrix());
+            // isReady trigger creation of effect if it doesnt exist yet
+            if (proceduralTexture.isReady()) {
+                proceduralTexture.render();
+                resolve(proceduralTexture);
+            }
+            else {
+                proceduralTexture.getEffect().executeWhenCompiled(function () {
+                    proceduralTexture.render();
+                    resolve(proceduralTexture);
+                });
+            }
+        });
+    };
+    return KHR_texture_transform;
+}());
+exports.KHR_texture_transform = KHR_texture_transform;
+glTFExporter_1._Exporter.RegisterExtension(NAME, function (exporter) { return new KHR_texture_transform(exporter); });
+
+
+/***/ }),
+
+/***/ "./src/glTF/2.0/Extensions/index.ts":
+/*!******************************************!*\
+  !*** ./src/glTF/2.0/Extensions/index.ts ***!
+  \******************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+function __export(m) {
+    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}
+Object.defineProperty(exports, "__esModule", { value: true });
+__export(__webpack_require__(/*! ./KHR_texture_transform */ "./src/glTF/2.0/Extensions/KHR_texture_transform.ts"));
+
+
+/***/ }),
+
 /***/ "./src/glTF/2.0/glTFAnimation.ts":
 /***/ "./src/glTF/2.0/glTFAnimation.ts":
 /*!***************************************!*\
 /*!***************************************!*\
   !*** ./src/glTF/2.0/glTFAnimation.ts ***!
   !*** ./src/glTF/2.0/glTFAnimation.ts ***!
@@ -3810,7 +3926,19 @@ __export(__webpack_require__(/*! ./glTFExporterExtension */ "./src/glTF/2.0/glTF
 __export(__webpack_require__(/*! ./glTFMaterialExporter */ "./src/glTF/2.0/glTFMaterialExporter.ts"));
 __export(__webpack_require__(/*! ./glTFMaterialExporter */ "./src/glTF/2.0/glTFMaterialExporter.ts"));
 __export(__webpack_require__(/*! ./glTFSerializer */ "./src/glTF/2.0/glTFSerializer.ts"));
 __export(__webpack_require__(/*! ./glTFSerializer */ "./src/glTF/2.0/glTFSerializer.ts"));
 __export(__webpack_require__(/*! ./glTFUtilities */ "./src/glTF/2.0/glTFUtilities.ts"));
 __export(__webpack_require__(/*! ./glTFUtilities */ "./src/glTF/2.0/glTFUtilities.ts"));
+__export(__webpack_require__(/*! ./Extensions */ "./src/glTF/2.0/Extensions/index.ts"));
+
+
+/***/ }),
+
+/***/ "./src/glTF/2.0/shaders/textureTransform.fragment.fx":
+/*!***********************************************************!*\
+  !*** ./src/glTF/2.0/shaders/textureTransform.fragment.fx ***!
+  \***********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
 
 
+module.exports = "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D textureSampler;\nuniform mat4 textureTransformMat;\nvoid main(void) {\nvec2 uvTransformed=(textureTransformMat*vec4(vUV.xy,1,1)).xy;\ngl_FragColor=texture2D(textureSampler,uvTransformed);\n}"
 
 
 /***/ }),
 /***/ }),
 
 

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.js.map


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js.map


+ 60 - 0
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -72,6 +72,7 @@ declare module 'babylonjs-serializers/src/glTF/2.0' {
     export * from "babylonjs-serializers/src/glTF/2.0/glTFMaterialExporter";
     export * from "babylonjs-serializers/src/glTF/2.0/glTFMaterialExporter";
     export * from "babylonjs-serializers/src/glTF/2.0/glTFSerializer";
     export * from "babylonjs-serializers/src/glTF/2.0/glTFSerializer";
     export * from "babylonjs-serializers/src/glTF/2.0/glTFUtilities";
     export * from "babylonjs-serializers/src/glTF/2.0/glTFUtilities";
+    export * from "babylonjs-serializers/src/glTF/2.0/Extensions";
 }
 }
 
 
 declare module 'babylonjs-serializers/src/glTF/2.0/glTFAnimation' {
 declare module 'babylonjs-serializers/src/glTF/2.0/glTFAnimation' {
@@ -613,6 +614,40 @@ declare module 'babylonjs-serializers/src/glTF/2.0/glTFUtilities' {
     }
     }
 }
 }
 
 
+declare module 'babylonjs-serializers/src/glTF/2.0/Extensions' {
+    export * from "babylonjs-serializers/src/glTF/2.0/Extensions/KHR_texture_transform";
+}
+
+declare module 'babylonjs-serializers/src/glTF/2.0/Extensions/KHR_texture_transform' {
+    import { Texture, Nullable, Vector2, Scene, BaseTexture } from "babylonjs";
+    import { ImageMimeType } from "babylonjs-gltf2interface";
+    import { IGLTFExporterExtensionV2 } from "babylonjs-serializers/src/glTF/2.0/glTFExporterExtension";
+    import { _Exporter } from "babylonjs-serializers/src/glTF/2.0/glTFExporter";
+    /**
+        * @hidden
+        */
+    export class KHR_texture_transform implements IGLTFExporterExtensionV2 {
+            /** Name of this extension */
+            readonly name: string;
+            /** Defines whether this extension is enabled */
+            enabled: boolean;
+            /** Defines whether this extension is required */
+            required: boolean;
+            constructor(exporter: _Exporter);
+            dispose(): void;
+            preExportTextureAsync(context: string, babylonTexture: Texture, mimeType: ImageMimeType): Nullable<Promise<Texture>>;
+            /**
+                * Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
+                * @param babylonTexture
+                * @param offset
+                * @param rotation
+                * @param scale
+                * @param scene
+                */
+            textureTransformTextureAsync(babylonTexture: Texture, offset: Vector2, rotation: number, scale: Vector2, scene: Scene): Promise<BaseTexture>;
+    }
+}
+
 
 
 /*BabylonJS serializers*/
 /*BabylonJS serializers*/
 // Dependencies for this module:
 // Dependencies for this module:
@@ -1173,4 +1208,29 @@ declare module BABYLON {
             static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
             static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
             static _NormalizeTangentFromRef(tangent: BABYLON.Vector4): void;
             static _NormalizeTangentFromRef(tangent: BABYLON.Vector4): void;
     }
     }
+}
+declare module BABYLON {
+    /**
+        * @hidden
+        */
+    export class KHR_texture_transform implements IGLTFExporterExtensionV2 {
+            /** Name of this extension */
+            readonly name: string;
+            /** Defines whether this extension is enabled */
+            enabled: boolean;
+            /** Defines whether this extension is required */
+            required: boolean;
+            constructor(exporter: _Exporter);
+            dispose(): void;
+            preExportTextureAsync(context: string, babylonTexture: BABYLON.Texture, mimeType: BABYLON.GLTF2.ImageMimeType): BABYLON.Nullable<Promise<BABYLON.Texture>>;
+            /**
+                * Transform the babylon texture by the offset, rotation and scale parameters using a procedural texture
+                * @param babylonTexture
+                * @param offset
+                * @param rotation
+                * @param scale
+                * @param scene
+                */
+            textureTransformTextureAsync(babylonTexture: BABYLON.Texture, offset: BABYLON.Vector2, rotation: number, scale: BABYLON.Vector2, scene: BABYLON.Scene): Promise<BABYLON.BaseTexture>;
+    }
 }
 }

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-serializers",
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.0.0-alpha.2",
+    "version": "4.0.0-alpha.4",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.2",
-        "babylonjs-gltf2interface": "4.0.0-alpha.2"
+        "babylonjs": "4.0.0-alpha.4",
+        "babylonjs-gltf2interface": "4.0.0-alpha.4"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 5 - 5
dist/preview release/viewer/babylon.viewer.d.ts

@@ -1,7 +1,7 @@
-/// <reference path="./babylon.d.ts"/>
-/// <reference path="./babylon.glTF2Interface.d.ts"/>
-/// <reference path="./babylonjs.loaders.d.ts"/>
-declare module "babylonjs-loaders"{ export=BABYLON;}
+/// <reference path="./babylon.d.ts"/>
+/// <reference path="./babylon.glTF2Interface.d.ts"/>
+/// <reference path="./babylonjs.loaders.d.ts"/>
+declare module "babylonjs-loaders"{ export=BABYLON;}
 // Generated by dts-bundle v0.7.3
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 // Dependencies for this module:
 //   ../../../../../Tools/Gulp/babylonjs
 //   ../../../../../Tools/Gulp/babylonjs
@@ -2469,4 +2469,4 @@ declare module BabylonViewer {
                 */
                 */
             static EnvironmentLODOffset: number;
             static EnvironmentLODOffset: number;
     }
     }
-}
+}

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


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


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

@@ -1,8 +1,8 @@
-/// <reference path="./babylon.d.ts"/>
-/// <reference path="./babylon.glTF2Interface.d.ts"/>
-/// <reference path="./babylonjs.loaders.d.ts"/>
-declare module "babylonjs-loaders"{ export=BABYLON;}
-
+/// <reference path="./babylon.d.ts"/>
+/// <reference path="./babylon.glTF2Interface.d.ts"/>
+/// <reference path="./babylonjs.loaders.d.ts"/>
+declare module "babylonjs-loaders"{ export=BABYLON;}
+
 // Generated by dts-bundle v0.7.3
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 // Dependencies for this module:
 //   ../../../../../Tools/Gulp/babylonjs
 //   ../../../../../Tools/Gulp/babylonjs

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

@@ -3,14 +3,15 @@
 ## Major updates
 ## Major updates
 
 
 - Added support for [parallel shader compilation](https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/) ([Deltakosh](https://github.com/deltakosh))
 - Added support for [parallel shader compilation](https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/) ([Deltakosh](https://github.com/deltakosh))
-@NEED DEMO- Added FlyCamera for free navigation in 3D space, with a limited set of settings ([Phuein](https://github.com/phuein))
-@NEED DEMO- Added Object Based Motion Blur post-process ([julien-moreau](https://github.com/julien-moreau))
-@NEED DEMO- WebXR ([TrevorDev](https://github.com/TrevorDev))
+- Added FlyCamera for free navigation in 3D space, with a limited set of settings ([Phuein](https://github.com/phuein)) [@NEED DEMO]
+- Added [Object Based Motion Blur](http://doc.babylonjs.com/how_to/using_motionblurpostprocess) post-process ([julien-moreau](https://github.com/julien-moreau))
+- WebXR ([TrevorDev](https://github.com/TrevorDev)) [@NEED DEMO]
   - Add customAnimationFrameRequester to allow sessions to hook into engine's render loop ([TrevorDev](https://github.com/TrevorDev))
   - Add customAnimationFrameRequester to allow sessions to hook into engine's render loop ([TrevorDev](https://github.com/TrevorDev))
   - camera customDefaultRenderTarget to allow cameras to render to a custom render target (eg. xr framebuffer) instead of the canvas ([TrevorDev](https://github.com/TrevorDev))
   - camera customDefaultRenderTarget to allow cameras to render to a custom render target (eg. xr framebuffer) instead of the canvas ([TrevorDev](https://github.com/TrevorDev))
   - webXR camera which can be updated by a webXRSession ([TrevorDev](https://github.com/TrevorDev))
   - webXR camera which can be updated by a webXRSession ([TrevorDev](https://github.com/TrevorDev))
   - webXRSessionManager to bridge xrSession to babylon's engine/camera ([TrevorDev](https://github.com/TrevorDev))
   - webXRSessionManager to bridge xrSession to babylon's engine/camera ([TrevorDev](https://github.com/TrevorDev))
   - webXRExperienceHelper to setup a default XR experience ([TrevorDev](https://github.com/TrevorDev))
   - webXRExperienceHelper to setup a default XR experience ([TrevorDev](https://github.com/TrevorDev))
+  - WebXREnterExitUI and WebXRManagedOutputCanvas classes to configure the XR experience ([TrevorDev](https://github.com/TrevorDev))
 
 
 ## Updates
 ## Updates
 
 
@@ -21,6 +22,7 @@
 
 
 ### Core Engine
 ### Core Engine
 
 
+- Improved shader precision detection ([Deltakosh](https://github.com/deltakosh))
 - Added support for bone matrix texture. Now skeletons will use a texture instead of uniforms when possible ([Deltakosh](https://github.com/deltakosh))
 - Added support for bone matrix texture. Now skeletons will use a texture instead of uniforms when possible ([Deltakosh](https://github.com/deltakosh))
 - Refactored of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
 - Refactored of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))

+ 1 - 1
gui/src/2D/controls/imageBasedSlider.ts

@@ -124,7 +124,7 @@ export class ImageBasedSlider extends BaseSlider {
                         this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition);
                         this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition);
                     }
                     }
                 } else {
                 } else {
-                    this._tempMeasure.copyFromFloats(left, top, thumbPosition, height);
+                    this._tempMeasure.copyFromFloats(left, top, thumbPosition + this._effectiveThumbThickness / 2, height);
                 }
                 }
                 this._valueBarImage._draw(this._tempMeasure, context);
                 this._valueBarImage._draw(this._tempMeasure, context);
             }
             }

+ 1 - 1
package.json

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

+ 1 - 1
readme.md

@@ -2,7 +2,7 @@
 
 
 Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
 Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
 
 
-[![Build Status](https://travis-ci.com/BabylonJS/Babylon.js.svg)](https://travis-ci.com/BabylonJS/Babylon.js)
+[![Build Status](https://travis-ci.com/BabylonJS/Babylon.js.svg?branch=master)](https://travis-ci.com/BabylonJS/Babylon.js)
 [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/babylonJS/babylon.js.svg)](https://isitmaintained.com/project/babylonJS/babylon.js "Average time to resolve an issue")
 [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/babylonJS/babylon.js.svg)](https://isitmaintained.com/project/babylonJS/babylon.js "Average time to resolve an issue")
 [![Percentage of issues still open](https://isitmaintained.com/badge/open/babylonJS/babylon.js.svg)](https://isitmaintained.com/project/babylonJS/babylon.js "Percentage of issues still open")
 [![Percentage of issues still open](https://isitmaintained.com/badge/open/babylonJS/babylon.js.svg)](https://isitmaintained.com/project/babylonJS/babylon.js "Percentage of issues still open")
 
 

+ 2 - 7
src/Cameras/Inputs/babylon.arcRotateCameraVRDeviceOrientationInput.ts

@@ -15,11 +15,6 @@ module BABYLON {
         public alphaCorrection = 1;
         public alphaCorrection = 1;
 
 
         /**
         /**
-         * Defines a correction factor applied on the beta value retrieved from the orientation events.
-         */
-        public betaCorrection = 1;
-
-        /**
          * Defines a correction factor applied on the gamma value retrieved from the orientation events.
          * Defines a correction factor applied on the gamma value retrieved from the orientation events.
          */
          */
         public gammaCorrection = 1;
         public gammaCorrection = 1;
@@ -50,11 +45,11 @@ module BABYLON {
         /** @hidden */
         /** @hidden */
         public _onOrientationEvent(evt: DeviceOrientationEvent): void {
         public _onOrientationEvent(evt: DeviceOrientationEvent): void {
             if (evt.alpha !== null) {
             if (evt.alpha !== null) {
-                this._alpha = +evt.alpha | 0;
+                this._alpha = (+evt.alpha | 0) * this.alphaCorrection;
             }
             }
 
 
             if (evt.gamma !== null) {
             if (evt.gamma !== null) {
-                this._gamma = +evt.gamma | 0;
+                this._gamma = (+evt.gamma | 0) * this.gammaCorrection;
             }
             }
             this._dirty = true;
             this._dirty = true;
         }
         }

+ 87 - 0
src/Cameras/XR/babylon.webXREnterExitUI.ts

@@ -0,0 +1,87 @@
+module BABYLON {
+    /**
+     * Options to create the webXR UI
+     */
+    export class WebXREnterExitUIOptions {
+        /**
+         * Context to enter xr with
+         */
+        outputCanvasContext?: Nullable<WebGLRenderingContext>;
+
+        /**
+         * User provided buttons to enable/disable WebXR. The system will provide default if not set
+         */
+        customButtons?: Array<{ element: HTMLElement, initializationOptions: XRSessionCreationOptions }>;
+    }
+    /**
+     * UI to allow the user to enter/exit XR mode
+     */
+    export class WebXREnterExitUI implements IDisposable {
+        private _overlay: HTMLDivElement;
+        private _buttons: Array<{ element: HTMLElement, initializationOptions: XRSessionCreationOptions }> = [];
+        /**
+         * Creates UI to allow the user to enter/exit XR mode
+         * @param scene the scene to add the ui to
+         * @param helper the xr experience helper to enter/exit xr with
+         * @param options options to configure the UI
+         * @returns the created ui
+         */
+        public static CreateAsync(scene: BABYLON.Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions) {
+            var ui = new WebXREnterExitUI(scene, options);
+            var supportedPromises = ui._buttons.map((btn) => {
+                return helper.supportsSession(btn.initializationOptions);
+            });
+            return Promise.all(supportedPromises).then((results) => {
+                results.forEach((supported, i) => {
+                    if (supported) {
+                        ui._overlay.appendChild(ui._buttons[i].element);
+                        ui._buttons[i].element.onclick = async() => {
+                            if (helper.state == BABYLON.WebXRState.IN_XR) {
+                                await helper.exitXR();
+                                return;
+                            } else if (helper.state == BABYLON.WebXRState.NOT_IN_XR) {
+                                await helper.enterXR(ui._buttons[i].initializationOptions, "eye-level");
+                            }
+                        };
+                    }
+                });
+            });
+        }
+
+        private constructor(private scene: BABYLON.Scene, options: WebXREnterExitUIOptions) {
+            this._overlay = document.createElement("div");
+            this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
+
+            if (options.customButtons) {
+                this._buttons = options.customButtons;
+            } else {
+                var hmdBtn = document.createElement("button");
+                hmdBtn.style.cssText = "color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-repeat:no-repeat; background-position: center; outline: none;";
+                hmdBtn.innerText = "HMD";
+                this._buttons.push({ element: hmdBtn, initializationOptions: { immersive: true } });
+
+                var windowBtn = document.createElement("button");
+                windowBtn.style.cssText = hmdBtn.style.cssText;
+                windowBtn.innerText = "Window";
+                this._buttons.push({ element: windowBtn, initializationOptions: { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext } });
+            }
+
+            var renderCanvas = scene.getEngine().getRenderingCanvas();
+            if (renderCanvas && renderCanvas.parentNode) {
+                renderCanvas.parentNode.appendChild(this._overlay);
+                scene.onDisposeObservable.addOnce(() => {
+                    this.dispose();
+                });
+            }
+        }
+        /**
+         * Disposes of the object
+         */
+        dispose() {
+            var renderCanvas = this.scene.getEngine().getRenderingCanvas();
+            if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
+                renderCanvas.parentNode.removeChild(this._overlay);
+            }
+        }
+    }
+}

+ 44 - 35
src/Cameras/XR/babylon.webXRExperienceHelper.ts

@@ -4,9 +4,13 @@ module BABYLON {
      */
      */
     export enum WebXRState {
     export enum WebXRState {
         /**
         /**
-         * Transitioning to/from being in XR mode
+         * Transitioning to being in XR mode
          */
          */
-        TRANSITION,
+        ENTERING_XR,
+        /**
+         * Transitioning to non XR mode
+         */
+        EXITING_XR,
         /**
         /**
          * In XR mode and presenting
          * In XR mode and presenting
          */
          */
@@ -35,6 +39,11 @@ module BABYLON {
          */
          */
         public state: WebXRState = WebXRState.NOT_IN_XR;
         public state: WebXRState = WebXRState.NOT_IN_XR;
 
 
+        private _setState(val: WebXRState) {
+            this.state = val;
+            this.onStateChangedObservable.notifyObservers(this.state);
+        }
+
         /**
         /**
          * Fires when the state of the experience helper has changed
          * Fires when the state of the experience helper has changed
          */
          */
@@ -45,18 +54,31 @@ module BABYLON {
         private _nonVRCamera: Nullable<Camera> = null;
         private _nonVRCamera: Nullable<Camera> = null;
         private _originalSceneAutoClear = true;
         private _originalSceneAutoClear = true;
 
 
-        private _outputCanvas: HTMLCanvasElement;
-        private _outputCanvasContext: WebGLRenderingContext;
+        private _supported = false;
+
+        /**
+         * Creates the experience helper
+         * @param scene the scene to attach the experience helper to
+         * @returns a promise for the experience helper
+         */
+        public static CreateAsync(scene: BABYLON.Scene): Promise<WebXRExperienceHelper> {
+            var helper = new WebXRExperienceHelper(scene);
+            return helper._sessionManager.initialize().then(() => {
+                helper._supported = true;
+                return helper;
+            }).catch(() => {
+                return helper;
+            });
+        }
 
 
         /**
         /**
          * Creates a WebXRExperienceHelper
          * Creates a WebXRExperienceHelper
          * @param scene The scene the helper should be created in
          * @param scene The scene the helper should be created in
          */
          */
-        constructor(private scene: BABYLON.Scene) {
+        private constructor(private scene: BABYLON.Scene) {
             this.camera = new BABYLON.WebXRCamera("", scene);
             this.camera = new BABYLON.WebXRCamera("", scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this.container = new AbstractMesh("", scene);
             this.container = new AbstractMesh("", scene);
-            this._sessionManager.initialize();
         }
         }
 
 
         /**
         /**
@@ -64,8 +86,7 @@ module BABYLON {
          * @returns promise that resolves after xr mode has exited
          * @returns promise that resolves after xr mode has exited
          */
          */
         public exitXR() {
         public exitXR() {
-            this.state = WebXRState.TRANSITION;
-            this.onStateChangedObservable.notifyObservers(this.state);
+            this._setState(WebXRState.EXITING_XR);
             return this._sessionManager.exitXR();
             return this._sessionManager.exitXR();
         }
         }
 
 
@@ -76,13 +97,7 @@ module BABYLON {
          * @returns promise that resolves after xr mode has entered
          * @returns promise that resolves after xr mode has entered
          */
          */
         public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
         public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
-            this.state = WebXRState.TRANSITION;
-            this.onStateChangedObservable.notifyObservers(this.state);
-
-            this._createCanvas();
-            if (!sessionCreationOptions.outputContext) {
-                sessionCreationOptions.outputContext = this._outputCanvasContext;
-            }
+            this._setState(WebXRState.ENTERING_XR);
 
 
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(() => {
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(() => {
                 // Cache pre xr scene settings
                 // Cache pre xr scene settings
@@ -107,39 +122,33 @@ module BABYLON {
                     this.scene.autoClear = this._originalSceneAutoClear;
                     this.scene.autoClear = this._originalSceneAutoClear;
                     this.scene.activeCamera = this._nonVRCamera;
                     this.scene.activeCamera = this._nonVRCamera;
                     this._sessionManager.onXRFrameObservable.clear();
                     this._sessionManager.onXRFrameObservable.clear();
-                    this._removeCanvas();
 
 
-                    this.state = WebXRState.NOT_IN_XR;
-                    this.onStateChangedObservable.notifyObservers(this.state);
+                    this._setState(WebXRState.NOT_IN_XR);
                 });
                 });
-                this.state = WebXRState.IN_XR;
-                this.onStateChangedObservable.notifyObservers(this.state);
+                this._setState(WebXRState.IN_XR);
             });
             });
         }
         }
 
 
         /**
         /**
+         * Checks if the creation options are supported by the xr session
+         * @param options creation options
+         * @returns true if supported
+         */
+        public supportsSession(options: XRSessionCreationOptions) {
+            if (!this._supported) {
+                return Promise.resolve(false);
+            }
+            return this._sessionManager.supportsSession(options);
+        }
+
+        /**
          * Disposes of the experience helper
          * Disposes of the experience helper
          */
          */
         public dispose() {
         public dispose() {
             this.camera.dispose();
             this.camera.dispose();
             this.container.dispose();
             this.container.dispose();
-            this._removeCanvas();
             this.onStateChangedObservable.clear();
             this.onStateChangedObservable.clear();
             this._sessionManager.dispose();
             this._sessionManager.dispose();
         }
         }
-
-        // create canvas used to mirror/vr xr content in fullscreen
-        private _createCanvas() {
-            this._removeCanvas();
-            this._outputCanvas = document.createElement('canvas');
-            this._outputCanvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
-            document.body.appendChild(this._outputCanvas);
-            this._outputCanvasContext = <any>this._outputCanvas.getContext('xrpresent');
-        }
-        private _removeCanvas() {
-            if (this._outputCanvas && document.body.contains(this._outputCanvas)) {
-                document.body.removeChild(this._outputCanvas);
-            }
-        }
     }
     }
 }
 }

+ 62 - 0
src/Cameras/XR/babylon.webXRManagedOutputCanvas.ts

@@ -0,0 +1,62 @@
+module BABYLON {
+    /**
+     * Creates a canvas that is added/removed from the webpage when entering/exiting XR
+     */
+    export class WebXRManagedOutputCanvas implements IDisposable {
+        private _canvas: Nullable<HTMLCanvasElement> = null;
+        /**
+         * xrpresent context of the canvas which can be used to display/mirror xr content
+         */
+        public canvasContext: Nullable<WebGLRenderingContext> = null;
+        /**
+         * Initializes the canvas to be added/removed upon entering/exiting xr
+         * @param helper the xr experience helper used to trigger adding/removing of the canvas
+         * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
+         */
+        public constructor(helper: WebXRExperienceHelper, canvas?: HTMLCanvasElement) {
+            if (!canvas) {
+                canvas = document.createElement('canvas');
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
+            }
+            this._setManagedOutputCanvas(canvas);
+            helper.onStateChangedObservable.add((stateInfo) => {
+                if (stateInfo == WebXRState.ENTERING_XR) {
+                    // The canvas is added to the screen before entering XR because currently the xr session must be initialized while the canvas is added render properly
+                    this._addCanvas();
+                }else if (helper.state == WebXRState.NOT_IN_XR) {
+                    this._removeCanvas();
+                }
+            });
+        }
+        /**
+         * Disposes of the object
+         */
+        public dispose() {
+            this._removeCanvas();
+            this._setManagedOutputCanvas(null);
+        }
+
+        private _setManagedOutputCanvas(canvas: Nullable<HTMLCanvasElement>) {
+            this._removeCanvas();
+            if (!canvas) {
+                this._canvas = null;
+                this.canvasContext = null;
+            }else {
+                this._canvas = canvas;
+                this.canvasContext = <any>this._canvas.getContext('xrpresent');
+            }
+        }
+
+        private _addCanvas() {
+            if (this._canvas) {
+                document.body.appendChild(this._canvas);
+            }
+        }
+
+        private _removeCanvas() {
+            if (this._canvas && document.body.contains(this._canvas)) {
+                document.body.removeChild(this._canvas);
+            }
+        }
+    }
+}

+ 15 - 2
src/Cameras/XR/babylon.webXRSessionManager.ts

@@ -34,7 +34,7 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
-         * Initializes the manager, this must be done with a user action (eg. button click event)
+         * Initializes the manager
          * After initialization enterXR can be called to start an XR session
          * After initialization enterXR can be called to start an XR session
          * @returns Promise which resolves after it is initialized
          * @returns Promise which resolves after it is initialized
          */
          */
@@ -52,7 +52,7 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
-         * Enters XR with the desired XR session options
+         * Enters XR with the desired XR session options, this must be done with a user action (eg. button click event)
          * @param sessionCreationOptions xr options to create the session with
          * @param sessionCreationOptions xr options to create the session with
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @returns Promise which resolves after it enters XR
          * @returns Promise which resolves after it enters XR
@@ -142,6 +142,19 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * Checks if a session would be supported for the creation options specified
+         * @param options creation options to check if they are supported
+         * @returns true if supported
+         */
+        public supportsSession(options: XRSessionCreationOptions) {
+            return this._xrDevice.supportsSession(options).then(() => {
+                return true;
+            }).catch((e) => {
+                return false;
+            });
+        }
+
+        /**
          * @hidden
          * @hidden
          * Converts the render layer of xrSession to a render target
          * Converts the render layer of xrSession to a render target
          * @param session session to create render target for
          * @param session session to create render target for

+ 1 - 1
src/Cameras/babylon.arcRotateCamera.ts

@@ -598,7 +598,7 @@ module BABYLON {
 
 
         protected _getTargetPosition(): Vector3 {
         protected _getTargetPosition(): Vector3 {
             if (this._targetHost && this._targetHost.getAbsolutePosition) {
             if (this._targetHost && this._targetHost.getAbsolutePosition) {
-                var pos: Vector3 = this._targetHost.getAbsolutePosition();
+                var pos: Vector3 = this._targetHost.absolutePosition;
                 if (this._targetBoundingCenter) {
                 if (this._targetBoundingCenter) {
                     pos.addToRef(this._targetBoundingCenter, this._target);
                     pos.addToRef(this._targetBoundingCenter, this._target);
                 } else {
                 } else {

+ 6 - 5
src/Engine/babylon.engine.ts

@@ -481,7 +481,7 @@ module BABYLON {
          * Returns the current version of the framework
          * Returns the current version of the framework
          */
          */
         public static get Version(): string {
         public static get Version(): string {
-            return "4.0.0-alpha.2";
+            return "4.0.0-alpha.4";
         }
         }
 
 
         /**
         /**
@@ -1354,7 +1354,7 @@ module BABYLON {
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
-            this._caps.highPrecisionShaderSupported = true;
+            this._caps.highPrecisionShaderSupported = false;
             this._caps.timerQuery = this._gl.getExtension('EXT_disjoint_timer_query_webgl2') || this._gl.getExtension("EXT_disjoint_timer_query");
             this._caps.timerQuery = this._gl.getExtension('EXT_disjoint_timer_query_webgl2') || this._gl.getExtension("EXT_disjoint_timer_query");
             if (this._caps.timerQuery) {
             if (this._caps.timerQuery) {
                 if (this._webGLVersion === 1) {
                 if (this._webGLVersion === 1) {
@@ -1461,10 +1461,11 @@ module BABYLON {
             if (this._caps.etc1) { this.texturesSupported.push('-etc1.ktx'); }
             if (this._caps.etc1) { this.texturesSupported.push('-etc1.ktx'); }
 
 
             if (this._gl.getShaderPrecisionFormat) {
             if (this._gl.getShaderPrecisionFormat) {
-                var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
+                var vertex_highp = this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER, this._gl.HIGH_FLOAT);
+                var fragment_highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
 
 
-                if (highp) {
-                    this._caps.highPrecisionShaderSupported = highp.precision !== 0;
+                if (vertex_highp && fragment_highp) {
+                    this._caps.highPrecisionShaderSupported = vertex_highp.precision !== 0 && fragment_highp.precision !== 0;
                 }
                 }
             }
             }
 
 

+ 16 - 0
src/Helpers/babylon.sceneHelpers.ts

@@ -52,6 +52,13 @@ module BABYLON {
          * @returns a new VREXperienceHelper
          * @returns a new VREXperienceHelper
          */
          */
         createDefaultVRExperience(webVROptions?: VRExperienceHelperOptions): VRExperienceHelper;
         createDefaultVRExperience(webVROptions?: VRExperienceHelperOptions): VRExperienceHelper;
+
+        /**
+         * Creates a new XREXperienceHelper
+         * @see http://doc.babylonjs.com/how_to/webxr
+         * @returns a promise for a new XREXperienceHelper
+         */
+        createDefaultXRExperienceAsync(): Promise<WebXRExperienceHelper>;
     }
     }
 
 
     Scene.prototype.createDefaultLight = function(replace = false): void {
     Scene.prototype.createDefaultLight = function(replace = false): void {
@@ -173,4 +180,13 @@ module BABYLON {
     Scene.prototype.createDefaultVRExperience = function(webVROptions: VRExperienceHelperOptions = {}): VRExperienceHelper {
     Scene.prototype.createDefaultVRExperience = function(webVROptions: VRExperienceHelperOptions = {}): VRExperienceHelper {
         return new VRExperienceHelper(this, webVROptions);
         return new VRExperienceHelper(this, webVROptions);
     };
     };
+
+    Scene.prototype.createDefaultXRExperienceAsync = function(): Promise<BABYLON.WebXRExperienceHelper> {
+        return BABYLON.WebXRExperienceHelper.CreateAsync(this).then((helper) => {
+            var outputCanvas = new BABYLON.WebXRManagedOutputCanvas(helper);
+            return BABYLON.WebXREnterExitUI.CreateAsync(this, helper, {outputCanvasContext: outputCanvas.canvasContext}).then((ui) => {
+                return helper;
+            });
+        });
+    };
 }
 }

+ 142 - 112
src/Math/babylon.math.ts

@@ -4232,7 +4232,7 @@ module BABYLON {
         }
         }
 
 
         /** @hidden */
         /** @hidden */
-        private _updateIdentityStatus(isIdentity: boolean, isIdentityDirty: boolean = false, isIdentity3x2: boolean = false, isIdentity3x2Dirty : boolean= true) {
+        private _updateIdentityStatus(isIdentity: boolean, isIdentityDirty: boolean = false, isIdentity3x2: boolean = false, isIdentity3x2Dirty: boolean = true) {
             this.updateFlag = Matrix._updateFlagSeed++;
             this.updateFlag = Matrix._updateFlagSeed++;
             this._isIdentity = isIdentity;
             this._isIdentity = isIdentity;
             this._isIdentity3x2 = isIdentity || isIdentity3x2;
             this._isIdentity3x2 = isIdentity || isIdentity3x2;
@@ -4258,9 +4258,9 @@ module BABYLON {
                 this._isIdentityDirty = false;
                 this._isIdentityDirty = false;
                 const m = this._m;
                 const m = this._m;
                 this._isIdentity = (
                 this._isIdentity = (
-                    m[0]  === 1.0 && m[1]  === 0.0 && m[2]  === 0.0 && m[3]  === 0.0 &&
-                    m[4]  === 0.0 && m[5]  === 1.0 && m[6]  === 0.0 && m[7]  === 0.0 &&
-                    m[8]  === 0.0 && m[9]  === 0.0 && m[10] === 1.0 && m[11] === 0.0 &&
+                    m[0] === 1.0 && m[1] === 0.0 && m[2] === 0.0 && m[3] === 0.0 &&
+                    m[4] === 0.0 && m[5] === 1.0 && m[6] === 0.0 && m[7] === 0.0 &&
+                    m[8] === 0.0 && m[9] === 0.0 && m[10] === 1.0 && m[11] === 0.0 &&
                     m[12] === 0.0 && m[13] === 0.0 && m[14] === 0.0 && m[15] === 1.0
                     m[12] === 0.0 && m[13] === 0.0 && m[14] === 0.0 && m[15] === 1.0
                 );
                 );
             }
             }
@@ -4300,19 +4300,31 @@ module BABYLON {
             }
             }
 
 
             const m = this._m;
             const m = this._m;
-            const temp1 = m[10] * m[15] - m[11] * m[14];
-            const temp2 = m[9] * m[15] - m[11] * m[13];
-            const temp3 = m[9] * m[14] - m[10] * m[13];
-            const temp4 = m[8] * m[15] - m[11] * m[12];
-            const temp5 = m[8] * m[14] - m[10] * m[12];
-            const temp6 = m[8] * m[13] - m[9] * m[12];
-
-            return (
-              m[0] * (m[5] * temp1 - m[6] * temp2 + m[7] * temp3) -
-              m[1] * (m[4] * temp1 - m[6] * temp4 + m[7] * temp5) +
-              m[2] * (m[4] * temp2 - m[5] * temp4 + m[7] * temp6) -
-              m[3] * (m[4] * temp3 - m[5] * temp5 + m[6] * temp6)
-            );
+            const m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
+            const m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
+            const m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
+            const m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
+            // https://en.wikipedia.org/wiki/Laplace_expansion
+            // to compute the deterrminant of a 4x4 Matrix we compute the cofactors of any row or column,
+            // then we multiply each Cofactor by its corresponding matrix value and sum them all to get the determinant
+            // Cofactor(i, j) = sign(i,j) * det(Minor(i, j))
+            // where
+            //  - sign(i,j) = (i+j) % 2 === 0 ? 1 : -1
+            //  - Minor(i, j) is the 3x3 matrix we get by removing row i and column j from current Matrix
+            //
+            // Here we do that for the 1st row.
+
+            const det_22_33 = m22 * m33 - m32 * m23;
+            const det_21_33 = m21 * m33 - m31 * m23;
+            const det_21_32 = m21 * m32 - m31 * m22;
+            const det_20_33 = m20 * m33 - m30 * m23;
+            const det_20_32 = m20 * m32 - m22 * m30;
+            const det_20_31 = m20 * m31 - m30 * m21;
+            const cofact_00 = +(m11 * det_22_33 - m12 * det_21_33 + m13 * det_21_32);
+            const cofact_01 = -(m10 * det_22_33 - m12 * det_20_33 + m13 * det_20_32);
+            const cofact_02 = +(m10 * det_21_33 - m11 * det_20_33 + m13 * det_20_31);
+            const cofact_03 = -(m10 * det_21_32 - m11 * det_20_32 + m12 * det_20_31);
+            return m00 * cofact_00 + m01 * cofact_01 + m02 * cofact_02 + m03 * cofact_03;
         }
         }
 
 
         // Methods
         // Methods
@@ -4405,55 +4417,70 @@ module BABYLON {
                 return this;
                 return this;
             }
             }
 
 
+            // the inverse of a Matrix is the transpose of cofactor matrix divided by the determinant
             const m = this._m;
             const m = this._m;
-            const l1 = m[0], l2 = m[1], l3 = m[2], l4 = m[3];
-            const l5 = m[4], l6 = m[5], l7 = m[6], l8 = m[7];
-            const l9 = m[8], l10 = m[9], l11 = m[10], l12 = m[11];
-            const l13 = m[12], l14 = m[13], l15 = m[14], l16 = m[15];
-
-            const l17 = l11 * l16 - l12 * l15;
-            const l18 = l10 * l16 - l12 * l14;
-            const l19 = l10 * l15 - l11 * l14;
-            const l20 = l9 * l16 - l12 * l13;
-            const l21 = l9 * l15 - l11 * l13;
-            const l22 = l9 * l14 - l10 * l13;
-            const l23 = l6 * l17 - l7 * l18 + l8 * l19;
-            const l24 = -(l5 * l17 - l7 * l20 + l8 * l21);
-            const l25 = l5 * l18 - l6 * l20 + l8 * l22;
-            const l26 = -(l5 * l19 - l6 * l21 + l7 * l22);
-            const l27 = 1 / (l1 * l23 + l2 * l24 + l3 * l25 + l4 * l26);
-            const l28 = l7 * l16 - l8 * l15;
-            const l29 = l6 * l16 - l8 * l14;
-            const l30 = l6 * l15 - l7 * l14;
-            const l31 = l5 * l16 - l8 * l13;
-            const l32 = l5 * l15 - l7 * l13;
-            const l33 = l5 * l14 - l6 * l13;
-            const l34 = l7 * l12 - l8 * l11;
-            const l35 = l6 * l12 - l8 * l10;
-            const l36 = l6 * l11 - l7 * l10;
-            const l37 = l5 * l12 - l8 * l9;
-            const l38 = l5 * l11 - l7 * l9;
-            const l39 = l5 * l10 - l6 * l9;
-
-            const otherM = other._m;
-            otherM[0] = l23 * l27;
-            otherM[4] = l24 * l27;
-            otherM[8] = l25 * l27;
-            otherM[12] = l26 * l27;
-            otherM[1] = -(l2 * l17 - l3 * l18 + l4 * l19) * l27;
-            otherM[5] = (l1 * l17 - l3 * l20 + l4 * l21) * l27;
-            otherM[9] = -(l1 * l18 - l2 * l20 + l4 * l22) * l27;
-            otherM[13] = (l1 * l19 - l2 * l21 + l3 * l22) * l27;
-            otherM[2] = (l2 * l28 - l3 * l29 + l4 * l30) * l27;
-            otherM[6] = -(l1 * l28 - l3 * l31 + l4 * l32) * l27;
-            otherM[10] = (l1 * l29 - l2 * l31 + l4 * l33) * l27;
-            otherM[14] = -(l1 * l30 - l2 * l32 + l3 * l33) * l27;
-            otherM[3] = -(l2 * l34 - l3 * l35 + l4 * l36) * l27;
-            otherM[7] = (l1 * l34 - l3 * l37 + l4 * l38) * l27;
-            otherM[11] = -(l1 * l35 - l2 * l37 + l4 * l39) * l27;
-            otherM[15] = (l1 * l36 - l2 * l38 + l3 * l39) * l27;
-
-            other._markAsUpdated();
+            const m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
+            const m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
+            const m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
+            const m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15];
+
+            const det_22_33 = m22 * m33 - m32 * m23;
+            const det_21_33 = m21 * m33 - m31 * m23;
+            const det_21_32 = m21 * m32 - m31 * m22;
+            const det_20_33 = m20 * m33 - m30 * m23;
+            const det_20_32 = m20 * m32 - m22 * m30;
+            const det_20_31 = m20 * m31 - m30 * m21;
+
+            const cofact_00 = +(m11 * det_22_33 - m12 * det_21_33 + m13 * det_21_32);
+            const cofact_01 = -(m10 * det_22_33 - m12 * det_20_33 + m13 * det_20_32);
+            const cofact_02 = +(m10 * det_21_33 - m11 * det_20_33 + m13 * det_20_31);
+            const cofact_03 = -(m10 * det_21_32 - m11 * det_20_32 + m12 * det_20_31);
+
+            const det = m00 * cofact_00 + m01 * cofact_01 + m02 * cofact_02 + m03 * cofact_03;
+
+            if (det === 0) {
+                // not invertible
+                other.copyFrom(this);
+                return this;
+            }
+
+            const detInv = 1 / det;
+            const det_12_33 = m12 * m33 - m32 * m13;
+            const det_11_33 = m11 * m33 - m31 * m13;
+            const det_11_32 = m11 * m32 - m31 * m12;
+            const det_10_33 = m10 * m33 - m30 * m13;
+            const det_10_32 = m10 * m32 - m30 * m12;
+            const det_10_31 = m10 * m31 - m30 * m11;
+            const det_12_23 = m12 * m23 - m22 * m13;
+            const det_11_23 = m11 * m23 - m21 * m13;
+            const det_11_22 = m11 * m22 - m21 * m12;
+            const det_10_23 = m10 * m23 - m20 * m13;
+            const det_10_22 = m10 * m22 - m20 * m12;
+            const det_10_21 = m10 * m21 - m20 * m11;
+
+            const cofact_10 = -(m01 * det_22_33 - m02 * det_21_33 + m03 * det_21_32);
+            const cofact_11 = +(m00 * det_22_33 - m02 * det_20_33 + m03 * det_20_32);
+            const cofact_12 = -(m00 * det_21_33 - m01 * det_20_33 + m03 * det_20_31);
+            const cofact_13 = +(m00 * det_21_32 - m01 * det_20_32 + m02 * det_20_31);
+
+            const cofact_20 = +(m01 * det_12_33 - m02 * det_11_33 + m03 * det_11_32);
+            const cofact_21 = -(m00 * det_12_33 - m02 * det_10_33 + m03 * det_10_32);
+            const cofact_22 = +(m00 * det_11_33 - m01 * det_10_33 + m03 * det_10_31);
+            const cofact_23 = -(m00 * det_11_32 - m01 * det_10_32 + m02 * det_10_31);
+
+            const cofact_30 = -(m01 * det_12_23 - m02 * det_11_23 + m03 * det_11_22);
+            const cofact_31 = +(m00 * det_12_23 - m02 * det_10_23 + m03 * det_10_22);
+            const cofact_32 = -(m00 * det_11_23 - m01 * det_10_23 + m03 * det_10_21);
+            const cofact_33 = +(m00 * det_11_22 - m01 * det_10_22 + m02 * det_10_21);
+
+            Matrix.FromValuesToRef(
+                cofact_00 * detInv, cofact_10 * detInv, cofact_20 * detInv, cofact_30 * detInv,
+                cofact_01 * detInv, cofact_11 * detInv, cofact_21 * detInv, cofact_31 * detInv,
+                cofact_02 * detInv, cofact_12 * detInv, cofact_22 * detInv, cofact_32 * detInv,
+                cofact_03 * detInv, cofact_13 * detInv, cofact_23 * detInv, cofact_33 * detInv,
+                other
+            );
+
             return this;
             return this;
         }
         }
 
 
@@ -4532,9 +4559,9 @@ module BABYLON {
         public removeRotationAndScaling(): Matrix {
         public removeRotationAndScaling(): Matrix {
             const m = this.m;
             const m = this.m;
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
-                  1.0,   0.0,   0.0,   0.0,
-                  0.0,   1.0,   0.0,   0.0,
-                  0.0,   0.0,   1.0,   0.0,
+                1.0, 0.0, 0.0, 0.0,
+                0.0, 1.0, 0.0, 0.0,
+                0.0, 0.0, 1.0, 0.0,
                 m[12], m[13], m[14], m[15],
                 m[12], m[13], m[14], m[15],
                 this
                 this
             );
             );
@@ -4619,7 +4646,7 @@ module BABYLON {
             var om8 = otherM[8], om9 = otherM[9], om10 = otherM[10], om11 = otherM[11];
             var om8 = otherM[8], om9 = otherM[9], om10 = otherM[10], om11 = otherM[11];
             var om12 = otherM[12], om13 = otherM[13], om14 = otherM[14], om15 = otherM[15];
             var om12 = otherM[12], om13 = otherM[13], om14 = otherM[14], om15 = otherM[15];
 
 
-            result[offset    ] = tm0 * om0 + tm1 * om4 + tm2 * om8 + tm3 * om12;
+            result[offset] = tm0 * om0 + tm1 * om4 + tm2 * om8 + tm3 * om12;
             result[offset + 1] = tm0 * om1 + tm1 * om5 + tm2 * om9 + tm3 * om13;
             result[offset + 1] = tm0 * om1 + tm1 * om5 + tm2 * om9 + tm3 * om13;
             result[offset + 2] = tm0 * om2 + tm1 * om6 + tm2 * om10 + tm3 * om14;
             result[offset + 2] = tm0 * om2 + tm1 * om6 + tm2 * om10 + tm3 * om14;
             result[offset + 3] = tm0 * om3 + tm1 * om7 + tm2 * om11 + tm3 * om15;
             result[offset + 3] = tm0 * om3 + tm1 * om7 + tm2 * om11 + tm3 * om15;
@@ -4661,9 +4688,9 @@ module BABYLON {
             const m = this.m;
             const m = this.m;
             const om = other.m;
             const om = other.m;
             return (
             return (
-                m[0]  === om[0]  && m[1]  === om[1]  && m[2]  === om[2]  && m[3]  === om[3] &&
-                m[4]  === om[4]  && m[5]  === om[5]  && m[6]  === om[6]  && m[7]  === om[7] &&
-                m[8]  === om[8]  && m[9]  === om[9]  && m[10] === om[10] && m[11] === om[11] &&
+                m[0] === om[0] && m[1] === om[1] && m[2] === om[2] && m[3] === om[3] &&
+                m[4] === om[4] && m[5] === om[5] && m[6] === om[6] && m[7] === om[7] &&
+                m[8] === om[8] && m[9] === om[9] && m[10] === om[10] && m[11] === om[11] &&
                 m[12] === om[12] && m[13] === om[13] && m[14] === om[14] && m[15] === om[15]
                 m[12] === om[12] && m[13] === om[13] && m[14] === om[14] && m[15] === om[15]
             );
             );
         }
         }
@@ -4743,10 +4770,10 @@ module BABYLON {
             if (rotation) {
             if (rotation) {
                 const sx = 1 / scale.x, sy = 1 / scale.y, sz = 1 / scale.z;
                 const sx = 1 / scale.x, sy = 1 / scale.y, sz = 1 / scale.z;
                 Matrix.FromValuesToRef(
                 Matrix.FromValuesToRef(
-                    m[0] * sx, m[1] * sx,  m[2] * sx, 0.0,
-                    m[4] * sy, m[5] * sy,  m[6] * sy, 0.0,
+                    m[0] * sx, m[1] * sx, m[2] * sx, 0.0,
+                    m[4] * sy, m[5] * sy, m[6] * sy, 0.0,
                     m[8] * sz, m[9] * sz, m[10] * sz, 0.0,
                     m[8] * sz, m[9] * sz, m[10] * sz, 0.0,
-                          0.0,       0.0,        0.0, 1.0,
+                    0.0, 0.0, 0.0, 1.0,
                     MathTmp.Matrix[0]
                     MathTmp.Matrix[0]
                 );
                 );
 
 
@@ -4869,10 +4896,10 @@ module BABYLON {
             tmp.transposeToRef(ref);
             tmp.transposeToRef(ref);
             var m = ref._m;
             var m = ref._m;
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
-                m[0], m[1],  m[2], 0.0,
-                m[4], m[5],  m[6], 0.0,
+                m[0], m[1], m[2], 0.0,
+                m[4], m[5], m[6], 0.0,
                 m[8], m[9], m[10], 0.0,
                 m[8], m[9], m[10], 0.0,
-                 0.0,  0.0,   0.0, 1.0,
+                0.0, 0.0, 0.0, 1.0,
                 ref
                 ref
             );
             );
         }
         }
@@ -4902,10 +4929,10 @@ module BABYLON {
             const m = this._m;
             const m = this._m;
             const sx = 1 / scale.x, sy = 1 / scale.y, sz = 1 / scale.z;
             const sx = 1 / scale.x, sy = 1 / scale.y, sz = 1 / scale.z;
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
-                m[0] * sx, m[1] * sx,  m[2] * sx, 0.0,
-                m[4] * sy, m[5] * sy,  m[6] * sy, 0.0,
+                m[0] * sx, m[1] * sx, m[2] * sx, 0.0,
+                m[4] * sy, m[5] * sy, m[6] * sy, 0.0,
                 m[8] * sz, m[9] * sz, m[10] * sz, 0.0,
                 m[8] * sz, m[9] * sz, m[10] * sz, 0.0,
-                      0.0,       0.0,        0.0, 1.0,
+                0.0, 0.0, 0.0, 1.0,
                 result
                 result
             );
             );
             return this;
             return this;
@@ -5155,8 +5182,8 @@ module BABYLON {
             var c = Math.cos(angle);
             var c = Math.cos(angle);
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
                 1.0, 0.0, 0.0, 0.0,
                 1.0, 0.0, 0.0, 0.0,
-                0.0,   c,   s, 0.0,
-                0.0,  -s,   c, 0.0,
+                0.0, c, s, 0.0,
+                0.0, -s, c, 0.0,
                 0.0, 0.0, 0.0, 1.0,
                 0.0, 0.0, 0.0, 1.0,
                 result
                 result
             );
             );
@@ -5184,14 +5211,14 @@ module BABYLON {
             var s = Math.sin(angle);
             var s = Math.sin(angle);
             var c = Math.cos(angle);
             var c = Math.cos(angle);
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
-                c, 0.0,  -s, 0.0,
-              0.0, 1.0, 0.0, 0.0,
-                s, 0.0,   c, 0.0,
-              0.0, 0.0, 0.0, 1.0,
-              result
-          );
+                c, 0.0, -s, 0.0,
+                0.0, 1.0, 0.0, 0.0,
+                s, 0.0, c, 0.0,
+                0.0, 0.0, 0.0, 1.0,
+                result
+            );
 
 
-          result._updateIdentityStatus(c === 1 && s === 0);
+            result._updateIdentityStatus(c === 1 && s === 0);
         }
         }
 
 
         /**
         /**
@@ -5214,11 +5241,11 @@ module BABYLON {
             var s = Math.sin(angle);
             var s = Math.sin(angle);
             var c = Math.cos(angle);
             var c = Math.cos(angle);
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
-                c,   s, 0.0, 0.0,
-               -s,   c, 0.0, 0.0,
-              0.0, 0.0, 1.0, 0.0,
-              0.0, 0.0, 0.0, 1.0,
-             result
+                c, s, 0.0, 0.0,
+                -s, c, 0.0, 0.0,
+                0.0, 0.0, 1.0, 0.0,
+                0.0, 0.0, 0.0, 1.0,
+                result
             );
             );
 
 
             result._updateIdentityStatus(c === 1 && s === 0);
             result._updateIdentityStatus(c === 1 && s === 0);
@@ -5264,6 +5291,9 @@ module BABYLON {
             m[10] = (axis.z * axis.z) * c1 + c;
             m[10] = (axis.z * axis.z) * c1 + c;
             m[11] = 0.0;
             m[11] = 0.0;
 
 
+            m[12] = 0.0;
+            m[13] = 0.0;
+            m[14] = 0.0;
             m[15] = 1.0;
             m[15] = 1.0;
 
 
             result._markAsUpdated();
             result._markAsUpdated();
@@ -5317,13 +5347,13 @@ module BABYLON {
         public static ScalingToRef(x: number, y: number, z: number, result: Matrix): void {
         public static ScalingToRef(x: number, y: number, z: number, result: Matrix): void {
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
                 x, 0.0, 0.0, 0.0,
                 x, 0.0, 0.0, 0.0,
-              0.0,   y, 0.0, 0.0,
-              0.0, 0.0,   z, 0.0,
-              0.0, 0.0, 0.0, 1.0,
-              result
-          );
+                0.0, y, 0.0, 0.0,
+                0.0, 0.0, z, 0.0,
+                0.0, 0.0, 0.0, 1.0,
+                result
+            );
 
 
-          result._updateIdentityStatus(x === 1 && y === 1 && z === 1);
+            result._updateIdentityStatus(x === 1 && y === 1 && z === 1);
         }
         }
 
 
         /**
         /**
@@ -5351,7 +5381,7 @@ module BABYLON {
                 1.0, 0.0, 0.0, 0.0,
                 1.0, 0.0, 0.0, 0.0,
                 0.0, 1.0, 0.0, 0.0,
                 0.0, 1.0, 0.0, 0.0,
                 0.0, 0.0, 1.0, 0.0,
                 0.0, 0.0, 1.0, 0.0,
-                  x,   y,   z, 1.0,
+                x, y, z, 1.0,
                 result
                 result
             );
             );
             result._updateIdentityStatus(x === 0 && y === 0 && z === 0);
             result._updateIdentityStatus(x === 0 && y === 0 && z === 0);
@@ -5486,7 +5516,7 @@ module BABYLON {
                 xAxis.x, yAxis.x, zAxis.x, 0.0,
                 xAxis.x, yAxis.x, zAxis.x, 0.0,
                 xAxis.y, yAxis.y, zAxis.y, 0.0,
                 xAxis.y, yAxis.y, zAxis.y, 0.0,
                 xAxis.z, yAxis.z, zAxis.z, 0.0,
                 xAxis.z, yAxis.z, zAxis.z, 0.0,
-                     ex,      ey,      ez, 1.0,
+                ex, ey, ez, 1.0,
                 result
                 result
             );
             );
         }
         }
@@ -5545,7 +5575,7 @@ module BABYLON {
                 xAxis.x, yAxis.x, zAxis.x, 0.0,
                 xAxis.x, yAxis.x, zAxis.x, 0.0,
                 xAxis.y, yAxis.y, zAxis.y, 0.0,
                 xAxis.y, yAxis.y, zAxis.y, 0.0,
                 xAxis.z, yAxis.z, zAxis.z, 0.0,
                 xAxis.z, yAxis.z, zAxis.z, 0.0,
-                     ex,      ey,      ez, 1.0,
+                ex, ey, ez, 1.0,
                 result
                 result
             );
             );
         }
         }
@@ -5844,10 +5874,10 @@ module BABYLON {
             var cy = viewport.y;
             var cy = viewport.y;
 
 
             var viewportMatrix = Matrix.FromValues(
             var viewportMatrix = Matrix.FromValues(
-                     cw / 2.0,           0.0,         0.0, 0.0,
-                          0.0,     -ch / 2.0,         0.0, 0.0,
-                          0.0,           0.0, zmax - zmin, 0.0,
-                cx + cw / 2.0, ch / 2.0 + cy,        zmin, 1.0);
+                cw / 2.0, 0.0, 0.0, 0.0,
+                0.0, -ch / 2.0, 0.0, 0.0,
+                0.0, 0.0, zmax - zmin, 0.0,
+                cx + cw / 2.0, ch / 2.0 + cy, zmin, 1.0);
 
 
             var matrix = MathTmp.Matrix[0];
             var matrix = MathTmp.Matrix[0];
             world.multiplyToRef(view, matrix);
             world.multiplyToRef(view, matrix);
@@ -5946,9 +5976,9 @@ module BABYLON {
             var temp2 = -2 * y;
             var temp2 = -2 * y;
             var temp3 = -2 * z;
             var temp3 = -2 * z;
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
-                  temp * x + 1,       temp2 * x,       temp3 * x, 0.0,
-                      temp * y,   temp2 * y + 1,       temp3 * y, 0.0,
-                      temp * z,       temp2 * z,   temp3 * z + 1, 0.0,
+                temp * x + 1, temp2 * x, temp3 * x, 0.0,
+                temp * y, temp2 * y + 1, temp3 * y, 0.0,
+                temp * z, temp2 * z, temp3 * z + 1, 0.0,
                 temp * plane.d, temp2 * plane.d, temp3 * plane.d, 1.0,
                 temp * plane.d, temp2 * plane.d, temp3 * plane.d, 1.0,
                 result
                 result
             );
             );
@@ -5966,7 +5996,7 @@ module BABYLON {
                 xaxis.x, xaxis.y, xaxis.z, 0.0,
                 xaxis.x, xaxis.y, xaxis.z, 0.0,
                 yaxis.x, yaxis.y, yaxis.z, 0.0,
                 yaxis.x, yaxis.y, yaxis.z, 0.0,
                 zaxis.x, zaxis.y, zaxis.z, 0.0,
                 zaxis.x, zaxis.y, zaxis.z, 0.0,
-                    0.0,     0.0,    0.0,  1.0,
+                0.0, 0.0, 0.0, 1.0,
                 result
                 result
             );
             );
         }
         }

+ 4 - 1
src/babylon.mixins.ts

@@ -181,6 +181,7 @@ interface WebGLUniformLocation {
 // WebXR
 // WebXR
 interface XRDevice {
 interface XRDevice {
     requestSession(options: XRSessionCreationOptions): Promise<XRSession>;
     requestSession(options: XRSessionCreationOptions): Promise<XRSession>;
+    supportsSession(options: XRSessionCreationOptions): Promise<void>;
 }
 }
 interface XRSession {
 interface XRSession {
     baseLayer: XRWebGLLayer;
     baseLayer: XRWebGLLayer;
@@ -191,7 +192,9 @@ interface XRSession {
     addEventListener: Function;
     addEventListener: Function;
 }
 }
 interface XRSessionCreationOptions {
 interface XRSessionCreationOptions {
-    outputContext: WebGLRenderingContext;
+    outputContext?: WebGLRenderingContext | null;
+    immersive?: boolean;
+    environmentIntegration?: boolean;
 }
 }
 interface XRLayer {
 interface XRLayer {
     getViewport: Function;
     getViewport: Function;