Browse Source

Merge remote-tracking branch 'upstream/master' into materialBlockDirty

Julien Barrois 6 năm trước cách đây
mục cha
commit
70d92ecfb7
41 tập tin đã thay đổi với 25865 bổ sung25153 xóa
  1. 14039 13958
      Playground/babylon.d.txt
  2. BIN
      Playground/textures/gui/backgroundImage-vertical.png
  3. BIN
      Playground/textures/gui/valueImage-vertical.png
  4. 2 1
      Tools/Gulp/config.json
  5. 10584 10505
      dist/preview release/babylon.d.ts
  6. 1 1
      dist/preview release/babylon.js
  7. 181 20
      dist/preview release/babylon.max.js
  8. 181 20
      dist/preview release/babylon.no-module.max.js
  9. 1 1
      dist/preview release/babylon.worker.js
  10. 183 22
      dist/preview release/es6.js
  11. 4 2
      dist/preview release/gui/babylon.gui.d.ts
  12. 1 1
      dist/preview release/gui/babylon.gui.js
  13. 1 1
      dist/preview release/gui/babylon.gui.min.js
  14. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  15. 8 4
      dist/preview release/gui/babylon.gui.module.d.ts
  16. 3 0
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  17. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  18. 3 0
      dist/preview release/loaders/babylon.glTFFileLoader.js
  19. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  20. 3 0
      dist/preview release/loaders/babylonjs.loaders.js
  21. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  22. 198 272
      dist/preview release/viewer/babylon.viewer.d.ts
  23. 1 1
      dist/preview release/viewer/babylon.viewer.js
  24. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  25. 205 283
      dist/preview release/viewer/babylon.viewer.module.d.ts
  26. 4 1
      dist/preview release/what's new.md
  27. 20 3
      gui/src/2D/controls/baseSlider.ts
  28. 23 7
      gui/src/2D/controls/imageBasedSlider.ts
  29. 1 16
      gui/src/2D/controls/slider.ts
  30. 4 0
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  31. 8 5
      sandbox/index.js
  32. 55 7
      src/Cameras/XR/babylon.webXREnterExitUI.ts
  33. 18 8
      src/Cameras/XR/babylon.webXRExperienceHelper.ts
  34. 104 0
      src/Cameras/XR/babylon.webXRInput.ts
  35. 1 1
      src/Cameras/XR/babylon.webXRManagedOutputCanvas.ts
  36. 5 5
      src/Cameras/XR/babylon.webXRSessionManager.ts
  37. 3 1
      src/Helpers/babylon.sceneHelpers.ts
  38. 6 1
      src/Lights/Shadows/babylon.shadowGenerator.ts
  39. 2 0
      src/babylon.mixins.ts
  40. BIN
      tests/validation/ReferenceImages/Sliders.png
  41. 6 0
      tests/validation/config.json

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 14039 - 13958
Playground/babylon.d.txt


BIN
Playground/textures/gui/backgroundImage-vertical.png


BIN
Playground/textures/gui/valueImage-vertical.png


+ 2 - 1
Tools/Gulp/config.json

@@ -1302,7 +1302,8 @@
                 "../../src/Cameras/XR/babylon.webXRSessionManager.js",
                 "../../src/Cameras/XR/babylon.webXRExperienceHelper.js",
                 "../../src/Cameras/XR/babylon.webXREnterExitUI.js",
-                "../../src/Cameras/XR/babylon.webXRManagedOutputCanvas.js"
+                "../../src/Cameras/XR/babylon.webXRManagedOutputCanvas.js",
+                "../../src/Cameras/XR/babylon.webXRInput.js"
             ],
             "dependUpon": [
                 "core",

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 10584 - 10505
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/babylon.js


+ 181 - 20
dist/preview release/babylon.max.js

@@ -77037,7 +77037,7 @@ var BABYLON;
             var scene = this._scene;
             var engine = scene.getEngine();
             var material = subMesh.getMaterial();
-            if (!material) {
+            if (!material || subMesh.verticesCount === 0) {
                 return;
             }
             // Culling
@@ -77075,6 +77075,9 @@ var BABYLON;
                     var skeleton = mesh.skeleton;
                     if (skeleton.isUsingTextureForMatrices) {
                         var boneTexture = skeleton.getTransformMatrixTexture();
+                        if (!boneTexture) {
+                            return;
+                        }
                         this._effect.setTexture("boneSampler", boneTexture);
                         this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
                     }
@@ -108609,7 +108612,7 @@ var BABYLON;
          * After initialization enterXR can be called to start an XR session
          * @returns Promise which resolves after it is initialized
          */
-        WebXRSessionManager.prototype.initialize = function () {
+        WebXRSessionManager.prototype.initializeAsync = function () {
             var _this = this;
             // Check if the browser supports webXR
             this._xrNavigator = navigator;
@@ -108628,7 +108631,7 @@ var BABYLON;
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @returns Promise which resolves after it enters XR
          */
-        WebXRSessionManager.prototype.enterXR = function (sessionCreationOptions, frameOfReferenceType) {
+        WebXRSessionManager.prototype.enterXRAsync = function (sessionCreationOptions, frameOfReferenceType) {
             var _this = this;
             // initialize session
             return this._xrDevice.requestSession(sessionCreationOptions).then(function (session) {
@@ -108669,7 +108672,7 @@ var BABYLON;
          * Stops the xrSession and restores the renderloop
          * @returns Promise which resolves after it exits XR
          */
-        WebXRSessionManager.prototype.exitXR = function () {
+        WebXRSessionManager.prototype.exitXRAsync = function () {
             return this._xrSession.end();
         };
         /**
@@ -108677,7 +108680,7 @@ var BABYLON;
          * @param ray ray to cast into the environment
          * @returns Promise which resolves with a collision point in the environment if it exists
          */
-        WebXRSessionManager.prototype.environmentPointHitTest = function (ray) {
+        WebXRSessionManager.prototype.environmentPointHitTestAsync = function (ray) {
             var _this = this;
             return new Promise(function (res, rej) {
                 // Compute left handed inputs to request hit test
@@ -108711,7 +108714,7 @@ var BABYLON;
          * @param options creation options to check if they are supported
          * @returns true if supported
          */
-        WebXRSessionManager.prototype.supportsSession = function (options) {
+        WebXRSessionManager.prototype.supportsSessionAsync = function (options) {
             return this._xrDevice.supportsSession(options).then(function () {
                 return true;
             }).catch(function (e) {
@@ -108810,7 +108813,7 @@ var BABYLON;
          */
         WebXRExperienceHelper.CreateAsync = function (scene) {
             var helper = new WebXRExperienceHelper(scene);
-            return helper._sessionManager.initialize().then(function () {
+            return helper._sessionManager.initializeAsync().then(function () {
                 helper._supported = true;
                 return helper;
             }).catch(function () {
@@ -108821,9 +108824,9 @@ var BABYLON;
          * Exits XR mode and returns the scene to its original state
          * @returns promise that resolves after xr mode has exited
          */
-        WebXRExperienceHelper.prototype.exitXR = function () {
+        WebXRExperienceHelper.prototype.exitXRAsync = function () {
             this._setState(WebXRState.EXITING_XR);
-            return this._sessionManager.exitXR();
+            return this._sessionManager.exitXRAsync();
         };
         /**
          * Enters XR mode (This must be done within a user interaction in most browsers eg. button click)
@@ -108831,10 +108834,10 @@ var BABYLON;
          * @param frameOfReference frame of reference of the XR session
          * @returns promise that resolves after xr mode has entered
          */
-        WebXRExperienceHelper.prototype.enterXR = function (sessionCreationOptions, frameOfReference) {
+        WebXRExperienceHelper.prototype.enterXRAsync = function (sessionCreationOptions, frameOfReference) {
             var _this = this;
             this._setState(WebXRState.ENTERING_XR);
-            return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(function () {
+            return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(function () {
                 // Cache pre xr scene settings
                 _this._originalSceneAutoClear = _this.scene.autoClear;
                 _this._nonVRCamera = _this.scene.activeCamera;
@@ -108859,15 +108862,23 @@ var BABYLON;
             });
         };
         /**
+         * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
+         * @param ray ray to cast into the environment
+         * @returns Promise which resolves with a collision point in the environment if it exists
+         */
+        WebXRExperienceHelper.prototype.environmentPointHitTestAsync = function (ray) {
+            return this._sessionManager.environmentPointHitTestAsync(ray);
+        };
+        /**
          * Checks if the creation options are supported by the xr session
          * @param options creation options
          * @returns true if supported
          */
-        WebXRExperienceHelper.prototype.supportsSession = function (options) {
+        WebXRExperienceHelper.prototype.supportsSessionAsync = function (options) {
             if (!this._supported) {
                 return Promise.resolve(false);
             }
-            return this._sessionManager.supportsSession(options);
+            return this._sessionManager.supportsSessionAsync(options);
         };
         /**
          * Disposes of the experience helper
@@ -108923,6 +108934,32 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
 var BABYLON;
 (function (BABYLON) {
     /**
+     * Button which can be used to enter a different mode of XR
+     */
+    var WebXREnterExitUIButton = /** @class */ (function () {
+        /**
+         * Creates a WebXREnterExitUIButton
+         * @param element button element
+         * @param initializationOptions XR initialization options for the button
+         */
+        function WebXREnterExitUIButton(
+        /** button element */
+        element, 
+        /** XR initialization options for the button */
+        initializationOptions) {
+            this.element = element;
+            this.initializationOptions = initializationOptions;
+        }
+        /**
+         * Overwritable function which can be used to update the button's visuals when the state changes
+         * @param activeButton the current active button in the UI
+         */
+        WebXREnterExitUIButton.prototype.update = function (activeButton) {
+        };
+        return WebXREnterExitUIButton;
+    }());
+    BABYLON.WebXREnterExitUIButton = WebXREnterExitUIButton;
+    /**
      * Options to create the webXR UI
      */
     var WebXREnterExitUIOptions = /** @class */ (function () {
@@ -108939,6 +108976,7 @@ var BABYLON;
             var _this = this;
             this.scene = scene;
             this._buttons = [];
+            this._activeButton = null;
             this._overlay = document.createElement("div");
             this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
             if (options.customButtons) {
@@ -108948,11 +108986,20 @@ var BABYLON;
                 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 } });
+                this._buttons.push(new WebXREnterExitUIButton(hmdBtn, { immersive: true, outputContext: options.outputCanvasContext }));
+                this._buttons[this._buttons.length - 1].update = function (activeButton) {
+                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                    this.element.innerText = activeButton === this ? "EXIT" : "HMD";
+                };
                 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 } });
+                this._buttons.push(new WebXREnterExitUIButton(windowBtn, { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext }));
+                this._buttons[this._buttons.length - 1].update = function (activeButton) {
+                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                    this.element.innerText = activeButton === this ? "EXIT" : "Window";
+                };
+                this._updateButtons(null);
             }
             var renderCanvas = scene.getEngine().getRenderingCanvas();
             if (renderCanvas && renderCanvas.parentNode) {
@@ -108973,7 +109020,12 @@ var BABYLON;
             var _this = this;
             var ui = new WebXREnterExitUI(scene, options);
             var supportedPromises = ui._buttons.map(function (btn) {
-                return helper.supportsSession(btn.initializationOptions);
+                return helper.supportsSessionAsync(btn.initializationOptions);
+            });
+            helper.onStateChangedObservable.add(function (state) {
+                if (state == BABYLON.WebXRState.NOT_IN_XR) {
+                    ui._updateButtons(null);
+                }
             });
             return Promise.all(supportedPromises).then(function (results) {
                 results.forEach(function (supported, i) {
@@ -108984,13 +109036,15 @@ var BABYLON;
                                 switch (_a.label) {
                                     case 0:
                                         if (!(helper.state == BABYLON.WebXRState.IN_XR)) return [3 /*break*/, 2];
-                                        return [4 /*yield*/, helper.exitXR()];
+                                        ui._updateButtons(null);
+                                        return [4 /*yield*/, helper.exitXRAsync()];
                                     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")];
+                                        ui._updateButtons(ui._buttons[i]);
+                                        return [4 /*yield*/, helper.enterXRAsync(ui._buttons[i].initializationOptions, "eye-level")];
                                     case 3:
                                         _a.sent();
                                         _a.label = 4;
@@ -109002,6 +109056,13 @@ var BABYLON;
                 });
             });
         };
+        WebXREnterExitUI.prototype._updateButtons = function (activeButton) {
+            var _this = this;
+            this._activeButton = activeButton;
+            this._buttons.forEach(function (b) {
+                b.update(_this._activeButton);
+            });
+        };
         /**
          * Disposes of the object
          */
@@ -109038,7 +109099,7 @@ var BABYLON;
             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;";
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #000000;";
             }
             this._setManagedOutputCanvas(canvas);
             helper.onStateChangedObservable.add(function (stateInfo) {
@@ -109086,6 +109147,104 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.webXRManagedOutputCanvas.js.map
 
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Represents an XR input
+     */
+    var WebXRController = /** @class */ (function () {
+        /**
+         * Creates the controller
+         * @see https://doc.babylonjs.com/how_to/webxr
+         * @param scene the scene which the controller should be associated to
+         */
+        function WebXRController(scene) {
+            this.pointer = new BABYLON.AbstractMesh("controllerPointer", scene);
+        }
+        /**
+         * Disposes of the object
+         */
+        WebXRController.prototype.dispose = function () {
+            if (this.grip) {
+                this.grip.dispose();
+            }
+            this.pointer.dispose();
+        };
+        return WebXRController;
+    }());
+    BABYLON.WebXRController = WebXRController;
+    /**
+     * XR input used to track XR inputs such as controllers/rays
+     */
+    var WebXRInput = /** @class */ (function () {
+        /**
+         * Initializes the WebXRInput
+         * @param helper experience helper which the input should be created for
+         */
+        function WebXRInput(helper) {
+            var _this = this;
+            this.helper = helper;
+            /**
+             * XR controllers being tracked
+             */
+            this.controllers = [];
+            this._tmpMatrix = new BABYLON.Matrix();
+            this._frameObserver = helper._sessionManager.onXRFrameObservable.add(function () {
+                if (!helper._sessionManager._currentXRFrame || !helper._sessionManager._currentXRFrame.getDevicePose) {
+                    return false;
+                }
+                var xrFrame = helper._sessionManager._currentXRFrame;
+                var inputSources = helper._sessionManager._xrSession.getInputSources();
+                inputSources.forEach(function (input, i) {
+                    var inputPose = xrFrame.getInputPose(input, helper._sessionManager._frameOfReference);
+                    if (inputPose) {
+                        if (_this.controllers.length <= i) {
+                            _this.controllers.push(new WebXRController(helper.container.getScene()));
+                        }
+                        var controller = _this.controllers[i];
+                        // Manage the grip if it exists
+                        if (inputPose.gripMatrix) {
+                            if (!controller.grip) {
+                                controller.grip = new BABYLON.AbstractMesh("controllerGrip", helper.container.getScene());
+                            }
+                            BABYLON.Matrix.FromFloat32ArrayToRefScaled(inputPose.gripMatrix, 0, 1, _this._tmpMatrix);
+                            if (!controller.grip.getScene().useRightHandedSystem) {
+                                _this._tmpMatrix.toggleModelMatrixHandInPlace();
+                            }
+                            if (!controller.grip.rotationQuaternion) {
+                                controller.grip.rotationQuaternion = new BABYLON.Quaternion();
+                            }
+                            _this._tmpMatrix.decompose(controller.grip.scaling, controller.grip.rotationQuaternion, controller.grip.position);
+                        }
+                        // Manager pointer of controller
+                        BABYLON.Matrix.FromFloat32ArrayToRefScaled(inputPose.targetRay.transformMatrix, 0, 1, _this._tmpMatrix);
+                        if (!controller.pointer.getScene().useRightHandedSystem) {
+                            _this._tmpMatrix.toggleModelMatrixHandInPlace();
+                        }
+                        if (!controller.pointer.rotationQuaternion) {
+                            controller.pointer.rotationQuaternion = new BABYLON.Quaternion();
+                        }
+                        _this._tmpMatrix.decompose(controller.pointer.scaling, controller.pointer.rotationQuaternion, controller.pointer.position);
+                    }
+                });
+            });
+        }
+        /**
+         * Disposes of the object
+         */
+        WebXRInput.prototype.dispose = function () {
+            this.controllers.forEach(function (c) {
+                c.dispose();
+            });
+            this.helper._sessionManager.onXRFrameObservable.remove(this._frameObserver);
+        };
+        return WebXRInput;
+    }());
+    BABYLON.WebXRInput = WebXRInput;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.webXRInput.js.map
+
 // 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
 // & on Seb Lee-Delisle original work: http://seb.ly/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/
@@ -119986,7 +120145,9 @@ var BABYLON;
         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 BABYLON.WebXREnterExitUI.CreateAsync(_this, helper, { outputCanvasContext: outputCanvas.canvasContext })
+                .then(function (ui) {
+                new BABYLON.WebXRInput(helper);
                 return helper;
             });
         });

+ 181 - 20
dist/preview release/babylon.no-module.max.js

@@ -77004,7 +77004,7 @@ var BABYLON;
             var scene = this._scene;
             var engine = scene.getEngine();
             var material = subMesh.getMaterial();
-            if (!material) {
+            if (!material || subMesh.verticesCount === 0) {
                 return;
             }
             // Culling
@@ -77042,6 +77042,9 @@ var BABYLON;
                     var skeleton = mesh.skeleton;
                     if (skeleton.isUsingTextureForMatrices) {
                         var boneTexture = skeleton.getTransformMatrixTexture();
+                        if (!boneTexture) {
+                            return;
+                        }
                         this._effect.setTexture("boneSampler", boneTexture);
                         this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
                     }
@@ -108576,7 +108579,7 @@ var BABYLON;
          * After initialization enterXR can be called to start an XR session
          * @returns Promise which resolves after it is initialized
          */
-        WebXRSessionManager.prototype.initialize = function () {
+        WebXRSessionManager.prototype.initializeAsync = function () {
             var _this = this;
             // Check if the browser supports webXR
             this._xrNavigator = navigator;
@@ -108595,7 +108598,7 @@ var BABYLON;
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @returns Promise which resolves after it enters XR
          */
-        WebXRSessionManager.prototype.enterXR = function (sessionCreationOptions, frameOfReferenceType) {
+        WebXRSessionManager.prototype.enterXRAsync = function (sessionCreationOptions, frameOfReferenceType) {
             var _this = this;
             // initialize session
             return this._xrDevice.requestSession(sessionCreationOptions).then(function (session) {
@@ -108636,7 +108639,7 @@ var BABYLON;
          * Stops the xrSession and restores the renderloop
          * @returns Promise which resolves after it exits XR
          */
-        WebXRSessionManager.prototype.exitXR = function () {
+        WebXRSessionManager.prototype.exitXRAsync = function () {
             return this._xrSession.end();
         };
         /**
@@ -108644,7 +108647,7 @@ var BABYLON;
          * @param ray ray to cast into the environment
          * @returns Promise which resolves with a collision point in the environment if it exists
          */
-        WebXRSessionManager.prototype.environmentPointHitTest = function (ray) {
+        WebXRSessionManager.prototype.environmentPointHitTestAsync = function (ray) {
             var _this = this;
             return new Promise(function (res, rej) {
                 // Compute left handed inputs to request hit test
@@ -108678,7 +108681,7 @@ var BABYLON;
          * @param options creation options to check if they are supported
          * @returns true if supported
          */
-        WebXRSessionManager.prototype.supportsSession = function (options) {
+        WebXRSessionManager.prototype.supportsSessionAsync = function (options) {
             return this._xrDevice.supportsSession(options).then(function () {
                 return true;
             }).catch(function (e) {
@@ -108777,7 +108780,7 @@ var BABYLON;
          */
         WebXRExperienceHelper.CreateAsync = function (scene) {
             var helper = new WebXRExperienceHelper(scene);
-            return helper._sessionManager.initialize().then(function () {
+            return helper._sessionManager.initializeAsync().then(function () {
                 helper._supported = true;
                 return helper;
             }).catch(function () {
@@ -108788,9 +108791,9 @@ var BABYLON;
          * Exits XR mode and returns the scene to its original state
          * @returns promise that resolves after xr mode has exited
          */
-        WebXRExperienceHelper.prototype.exitXR = function () {
+        WebXRExperienceHelper.prototype.exitXRAsync = function () {
             this._setState(WebXRState.EXITING_XR);
-            return this._sessionManager.exitXR();
+            return this._sessionManager.exitXRAsync();
         };
         /**
          * Enters XR mode (This must be done within a user interaction in most browsers eg. button click)
@@ -108798,10 +108801,10 @@ var BABYLON;
          * @param frameOfReference frame of reference of the XR session
          * @returns promise that resolves after xr mode has entered
          */
-        WebXRExperienceHelper.prototype.enterXR = function (sessionCreationOptions, frameOfReference) {
+        WebXRExperienceHelper.prototype.enterXRAsync = function (sessionCreationOptions, frameOfReference) {
             var _this = this;
             this._setState(WebXRState.ENTERING_XR);
-            return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(function () {
+            return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(function () {
                 // Cache pre xr scene settings
                 _this._originalSceneAutoClear = _this.scene.autoClear;
                 _this._nonVRCamera = _this.scene.activeCamera;
@@ -108826,15 +108829,23 @@ var BABYLON;
             });
         };
         /**
+         * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
+         * @param ray ray to cast into the environment
+         * @returns Promise which resolves with a collision point in the environment if it exists
+         */
+        WebXRExperienceHelper.prototype.environmentPointHitTestAsync = function (ray) {
+            return this._sessionManager.environmentPointHitTestAsync(ray);
+        };
+        /**
          * Checks if the creation options are supported by the xr session
          * @param options creation options
          * @returns true if supported
          */
-        WebXRExperienceHelper.prototype.supportsSession = function (options) {
+        WebXRExperienceHelper.prototype.supportsSessionAsync = function (options) {
             if (!this._supported) {
                 return Promise.resolve(false);
             }
-            return this._sessionManager.supportsSession(options);
+            return this._sessionManager.supportsSessionAsync(options);
         };
         /**
          * Disposes of the experience helper
@@ -108890,6 +108901,32 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
 var BABYLON;
 (function (BABYLON) {
     /**
+     * Button which can be used to enter a different mode of XR
+     */
+    var WebXREnterExitUIButton = /** @class */ (function () {
+        /**
+         * Creates a WebXREnterExitUIButton
+         * @param element button element
+         * @param initializationOptions XR initialization options for the button
+         */
+        function WebXREnterExitUIButton(
+        /** button element */
+        element, 
+        /** XR initialization options for the button */
+        initializationOptions) {
+            this.element = element;
+            this.initializationOptions = initializationOptions;
+        }
+        /**
+         * Overwritable function which can be used to update the button's visuals when the state changes
+         * @param activeButton the current active button in the UI
+         */
+        WebXREnterExitUIButton.prototype.update = function (activeButton) {
+        };
+        return WebXREnterExitUIButton;
+    }());
+    BABYLON.WebXREnterExitUIButton = WebXREnterExitUIButton;
+    /**
      * Options to create the webXR UI
      */
     var WebXREnterExitUIOptions = /** @class */ (function () {
@@ -108906,6 +108943,7 @@ var BABYLON;
             var _this = this;
             this.scene = scene;
             this._buttons = [];
+            this._activeButton = null;
             this._overlay = document.createElement("div");
             this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
             if (options.customButtons) {
@@ -108915,11 +108953,20 @@ var BABYLON;
                 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 } });
+                this._buttons.push(new WebXREnterExitUIButton(hmdBtn, { immersive: true, outputContext: options.outputCanvasContext }));
+                this._buttons[this._buttons.length - 1].update = function (activeButton) {
+                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                    this.element.innerText = activeButton === this ? "EXIT" : "HMD";
+                };
                 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 } });
+                this._buttons.push(new WebXREnterExitUIButton(windowBtn, { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext }));
+                this._buttons[this._buttons.length - 1].update = function (activeButton) {
+                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                    this.element.innerText = activeButton === this ? "EXIT" : "Window";
+                };
+                this._updateButtons(null);
             }
             var renderCanvas = scene.getEngine().getRenderingCanvas();
             if (renderCanvas && renderCanvas.parentNode) {
@@ -108940,7 +108987,12 @@ var BABYLON;
             var _this = this;
             var ui = new WebXREnterExitUI(scene, options);
             var supportedPromises = ui._buttons.map(function (btn) {
-                return helper.supportsSession(btn.initializationOptions);
+                return helper.supportsSessionAsync(btn.initializationOptions);
+            });
+            helper.onStateChangedObservable.add(function (state) {
+                if (state == BABYLON.WebXRState.NOT_IN_XR) {
+                    ui._updateButtons(null);
+                }
             });
             return Promise.all(supportedPromises).then(function (results) {
                 results.forEach(function (supported, i) {
@@ -108951,13 +109003,15 @@ var BABYLON;
                                 switch (_a.label) {
                                     case 0:
                                         if (!(helper.state == BABYLON.WebXRState.IN_XR)) return [3 /*break*/, 2];
-                                        return [4 /*yield*/, helper.exitXR()];
+                                        ui._updateButtons(null);
+                                        return [4 /*yield*/, helper.exitXRAsync()];
                                     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")];
+                                        ui._updateButtons(ui._buttons[i]);
+                                        return [4 /*yield*/, helper.enterXRAsync(ui._buttons[i].initializationOptions, "eye-level")];
                                     case 3:
                                         _a.sent();
                                         _a.label = 4;
@@ -108969,6 +109023,13 @@ var BABYLON;
                 });
             });
         };
+        WebXREnterExitUI.prototype._updateButtons = function (activeButton) {
+            var _this = this;
+            this._activeButton = activeButton;
+            this._buttons.forEach(function (b) {
+                b.update(_this._activeButton);
+            });
+        };
         /**
          * Disposes of the object
          */
@@ -109005,7 +109066,7 @@ var BABYLON;
             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;";
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #000000;";
             }
             this._setManagedOutputCanvas(canvas);
             helper.onStateChangedObservable.add(function (stateInfo) {
@@ -109053,6 +109114,104 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.webXRManagedOutputCanvas.js.map
 
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Represents an XR input
+     */
+    var WebXRController = /** @class */ (function () {
+        /**
+         * Creates the controller
+         * @see https://doc.babylonjs.com/how_to/webxr
+         * @param scene the scene which the controller should be associated to
+         */
+        function WebXRController(scene) {
+            this.pointer = new BABYLON.AbstractMesh("controllerPointer", scene);
+        }
+        /**
+         * Disposes of the object
+         */
+        WebXRController.prototype.dispose = function () {
+            if (this.grip) {
+                this.grip.dispose();
+            }
+            this.pointer.dispose();
+        };
+        return WebXRController;
+    }());
+    BABYLON.WebXRController = WebXRController;
+    /**
+     * XR input used to track XR inputs such as controllers/rays
+     */
+    var WebXRInput = /** @class */ (function () {
+        /**
+         * Initializes the WebXRInput
+         * @param helper experience helper which the input should be created for
+         */
+        function WebXRInput(helper) {
+            var _this = this;
+            this.helper = helper;
+            /**
+             * XR controllers being tracked
+             */
+            this.controllers = [];
+            this._tmpMatrix = new BABYLON.Matrix();
+            this._frameObserver = helper._sessionManager.onXRFrameObservable.add(function () {
+                if (!helper._sessionManager._currentXRFrame || !helper._sessionManager._currentXRFrame.getDevicePose) {
+                    return false;
+                }
+                var xrFrame = helper._sessionManager._currentXRFrame;
+                var inputSources = helper._sessionManager._xrSession.getInputSources();
+                inputSources.forEach(function (input, i) {
+                    var inputPose = xrFrame.getInputPose(input, helper._sessionManager._frameOfReference);
+                    if (inputPose) {
+                        if (_this.controllers.length <= i) {
+                            _this.controllers.push(new WebXRController(helper.container.getScene()));
+                        }
+                        var controller = _this.controllers[i];
+                        // Manage the grip if it exists
+                        if (inputPose.gripMatrix) {
+                            if (!controller.grip) {
+                                controller.grip = new BABYLON.AbstractMesh("controllerGrip", helper.container.getScene());
+                            }
+                            BABYLON.Matrix.FromFloat32ArrayToRefScaled(inputPose.gripMatrix, 0, 1, _this._tmpMatrix);
+                            if (!controller.grip.getScene().useRightHandedSystem) {
+                                _this._tmpMatrix.toggleModelMatrixHandInPlace();
+                            }
+                            if (!controller.grip.rotationQuaternion) {
+                                controller.grip.rotationQuaternion = new BABYLON.Quaternion();
+                            }
+                            _this._tmpMatrix.decompose(controller.grip.scaling, controller.grip.rotationQuaternion, controller.grip.position);
+                        }
+                        // Manager pointer of controller
+                        BABYLON.Matrix.FromFloat32ArrayToRefScaled(inputPose.targetRay.transformMatrix, 0, 1, _this._tmpMatrix);
+                        if (!controller.pointer.getScene().useRightHandedSystem) {
+                            _this._tmpMatrix.toggleModelMatrixHandInPlace();
+                        }
+                        if (!controller.pointer.rotationQuaternion) {
+                            controller.pointer.rotationQuaternion = new BABYLON.Quaternion();
+                        }
+                        _this._tmpMatrix.decompose(controller.pointer.scaling, controller.pointer.rotationQuaternion, controller.pointer.position);
+                    }
+                });
+            });
+        }
+        /**
+         * Disposes of the object
+         */
+        WebXRInput.prototype.dispose = function () {
+            this.controllers.forEach(function (c) {
+                c.dispose();
+            });
+            this.helper._sessionManager.onXRFrameObservable.remove(this._frameObserver);
+        };
+        return WebXRInput;
+    }());
+    BABYLON.WebXRInput = WebXRInput;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.webXRInput.js.map
+
 // 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
 // & on Seb Lee-Delisle original work: http://seb.ly/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/
@@ -119953,7 +120112,9 @@ var BABYLON;
         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 BABYLON.WebXREnterExitUI.CreateAsync(_this, helper, { outputCanvasContext: outputCanvas.canvasContext })
+                .then(function (ui) {
+                new BABYLON.WebXRInput(helper);
                 return helper;
             });
         });

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/babylon.worker.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 183 - 22
dist/preview release/es6.js


+ 4 - 2
dist/preview release/gui/babylon.gui.d.ts

@@ -2082,6 +2082,7 @@ declare module BABYLON.GUI {
             name?: string | undefined;
             protected _thumbWidth: ValueAndUnit;
             protected _barOffset: ValueAndUnit;
+            protected _displayThumb: boolean;
             protected _effectiveBarOffset: number;
             protected _renderLeft: number;
             protected _renderTop: number;
@@ -2092,6 +2093,8 @@ declare module BABYLON.GUI {
             protected _effectiveThumbThickness: number;
             /** BABYLON.Observable raised when the sldier value changes */
             onValueChangedObservable: BABYLON.Observable<number>;
+            /** Gets or sets a boolean indicating if the thumb must be rendered */
+            displayThumb: boolean;
             /** Gets or sets main bar offset (ie. the margin applied to the value bar) */
             barOffset: string | number;
             /** Gets main bar offset in pixels*/
@@ -2130,8 +2133,6 @@ declare module BABYLON.GUI {
         */
     export class Slider extends BaseSlider {
             name?: string | undefined;
-            /** Gets or sets a boolean indicating if the thumb must be rendered */
-            displayThumb: boolean;
             /** Gets or sets border color */
             borderColor: string;
             /** Gets or sets background color */
@@ -2153,6 +2154,7 @@ declare module BABYLON.GUI {
         */
     export class ImageBasedSlider extends BaseSlider {
             name?: string | undefined;
+            displayThumb: boolean;
             /**
                 * Gets or sets the image used to render the background
                 */

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/gui/babylon.gui.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


+ 8 - 4
dist/preview release/gui/babylon.gui.module.d.ts

@@ -2246,6 +2246,7 @@ declare module 'babylonjs-gui/2D/controls/baseSlider' {
             name?: string | undefined;
             protected _thumbWidth: ValueAndUnit;
             protected _barOffset: ValueAndUnit;
+            protected _displayThumb: boolean;
             protected _effectiveBarOffset: number;
             protected _renderLeft: number;
             protected _renderTop: number;
@@ -2256,6 +2257,8 @@ declare module 'babylonjs-gui/2D/controls/baseSlider' {
             protected _effectiveThumbThickness: number;
             /** Observable raised when the sldier value changes */
             onValueChangedObservable: Observable<number>;
+            /** Gets or sets a boolean indicating if the thumb must be rendered */
+            displayThumb: boolean;
             /** Gets or sets main bar offset (ie. the margin applied to the value bar) */
             barOffset: string | number;
             /** Gets main bar offset in pixels*/
@@ -2297,8 +2300,6 @@ declare module 'babylonjs-gui/2D/controls/slider' {
         */
     export class Slider extends BaseSlider {
             name?: string | undefined;
-            /** Gets or sets a boolean indicating if the thumb must be rendered */
-            displayThumb: boolean;
             /** Gets or sets border color */
             borderColor: string;
             /** Gets or sets background color */
@@ -2324,6 +2325,7 @@ declare module 'babylonjs-gui/2D/controls/imageBasedSlider' {
         */
     export class ImageBasedSlider extends BaseSlider {
             name?: string | undefined;
+            displayThumb: boolean;
             /**
                 * Gets or sets the image used to render the background
                 */
@@ -5008,6 +5010,7 @@ declare module BABYLON.GUI {
             name?: string | undefined;
             protected _thumbWidth: ValueAndUnit;
             protected _barOffset: ValueAndUnit;
+            protected _displayThumb: boolean;
             protected _effectiveBarOffset: number;
             protected _renderLeft: number;
             protected _renderTop: number;
@@ -5018,6 +5021,8 @@ declare module BABYLON.GUI {
             protected _effectiveThumbThickness: number;
             /** BABYLON.Observable raised when the sldier value changes */
             onValueChangedObservable: BABYLON.Observable<number>;
+            /** Gets or sets a boolean indicating if the thumb must be rendered */
+            displayThumb: boolean;
             /** Gets or sets main bar offset (ie. the margin applied to the value bar) */
             barOffset: string | number;
             /** Gets main bar offset in pixels*/
@@ -5056,8 +5061,6 @@ declare module BABYLON.GUI {
         */
     export class Slider extends BaseSlider {
             name?: string | undefined;
-            /** Gets or sets a boolean indicating if the thumb must be rendered */
-            displayThumb: boolean;
             /** Gets or sets border color */
             borderColor: string;
             /** Gets or sets background color */
@@ -5079,6 +5082,7 @@ declare module BABYLON.GUI {
         */
     export class ImageBasedSlider extends BaseSlider {
             name?: string | undefined;
+            displayThumb: boolean;
             /**
                 * Gets or sets the image used to render the background
                 */

+ 3 - 0
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -1637,6 +1637,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var _this = this;
+                if (channel.target.node == undefined) {
+                    return Promise.resolve();
+                }
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 // Ignore animations that have no animation targets.
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 3 - 0
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3845,6 +3845,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var _this = this;
+                if (channel.target.node == undefined) {
+                    return Promise.resolve();
+                }
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 // Ignore animations that have no animation targets.
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 3 - 0
dist/preview release/loaders/babylonjs.loaders.js

@@ -4907,6 +4907,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var _this = this;
+                if (channel.target.node == undefined) {
+                    return Promise.resolve();
+                }
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 // Ignore animations that have no animation targets.
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.min.js


+ 198 - 272
dist/preview release/viewer/babylon.viewer.d.ts

@@ -123,80 +123,6 @@ declare module BabylonViewer {
     export let viewerManager: ViewerManager;
 }
 declare module BabylonViewer {
-    /**
-        * The Default viewer is the default implementation of the AbstractViewer.
-        * It uses the templating system to render a new canvas and controls.
-        */
-    export class DefaultViewer extends AbstractViewer {
-            containerElement: Element;
-            fullscreenElement?: Element;
-            /**
-                * Create a new default viewer
-                * @param containerElement the element in which the templates will be rendered
-                * @param initialConfiguration the initial configuration. Defaults to extending the default configuration
-                */
-            constructor(containerElement: Element, initialConfiguration?: ViewerConfiguration);
-            registerTemplatePlugin(plugin: IViewerTemplatePlugin): void;
-            /**
-                * This will be executed when the templates initialize.
-                */
-            protected _onTemplatesLoaded(): Promise<AbstractViewer>;
-            protected _initVR(): void;
-            /**
-                * Toggle fullscreen of the entire viewer
-                */
-            toggleFullscreen: () => void;
-            /**
-                * Preparing the container element to present the viewer
-                */
-            protected _prepareContainerElement(): void;
-            /**
-                * This function will configure the templates and update them after a model was loaded
-                * It is mainly responsible to changing the title and subtitle etc'.
-                * @param model the model to be used to configure the templates by
-                */
-            protected _configureTemplate(model?: ViewerModel): void;
-            /**
-                * This will load a new model to the default viewer
-                * overriding the AbstractViewer's loadModel.
-                * The scene will automatically be cleared of the old models, if exist.
-                * @param model the configuration object (or URL) to load.
-                */
-            loadModel(model?: string | File | IModelConfiguration): Promise<ViewerModel>;
-            /**
-                * Show the overlay and the defined sub-screen.
-                * Mainly used for help and errors
-                * @param subScreen the name of the subScreen. Those can be defined in the configuration object
-                */
-            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
-            /**
-                * Hide the overlay screen.
-                */
-            hideOverlayScreen(): Promise<string> | Promise<Template>;
-            /**
-                * show the viewer (in case it was hidden)
-                *
-                * @param visibilityFunction an optional function to execute in order to show the container
-                */
-            show(visibilityFunction?: ((template: Template) => Promise<Template>)): Promise<Template>;
-            /**
-                * hide the viewer (in case it is visible)
-                *
-                * @param visibilityFunction an optional function to execute in order to hide the container
-                */
-            hide(visibilityFunction?: ((template: Template) => Promise<Template>)): Promise<Template>;
-            /**
-                * Show the loading screen.
-                * The loading screen can be configured using the configuration object
-                */
-            showLoadingScreen(): Promise<string> | Promise<Template>;
-            /**
-                * Hide the loading screen
-                */
-            hideLoadingScreen(): Promise<string> | Promise<Template>;
-            dispose(): void;
-            protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
-    }
 }
 declare module BabylonViewer {
     /**
@@ -1038,171 +964,6 @@ declare module BabylonViewer {
     }
 }
 declare module BabylonViewer {
-    /**
-        * The object sent when an event is triggered
-        */
-    export interface EventCallback {
-            event: Event;
-            template: Template;
-            selector: string;
-            payload?: any;
-    }
-    /**
-        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
-        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
-        */
-    export class TemplateManager {
-            containerElement: Element;
-            /**
-                * Will be triggered when any template is initialized
-                */
-            onTemplateInit: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when any template is fully loaded
-                */
-            onTemplateLoaded: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when a template state changes
-                */
-            onTemplateStateChange: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when all templates finished loading
-                */
-            onAllLoaded: BABYLON.Observable<TemplateManager>;
-            /**
-                * Will be triggered when any event on any template is triggered.
-                */
-            onEventTriggered: BABYLON.Observable<EventCallback>;
-            /**
-                * This template manager's event manager. In charge of callback registrations to native event types
-                */
-            eventManager: EventManager;
-            constructor(containerElement: Element);
-            /**
-                * Initialize the template(s) for the viewer. Called bay the Viewer class
-                * @param templates the templates to be used to initialize the main template
-                */
-            initTemplate(templates: {
-                    [key: string]: ITemplateConfiguration;
-            }): Promise<void>;
-            /**
-                * Get the canvas in the template tree.
-                * There must be one and only one canvas inthe template.
-                */
-            getCanvas(): HTMLCanvasElement | null;
-            /**
-                * Get a specific template from the template tree
-                * @param name the name of the template to load
-                */
-            getTemplate(name: string): Template | undefined;
-            /**
-                * Dispose the template manager
-                */
-            dispose(): void;
-    }
-    /**
-        * This class represents a single template in the viewer's template tree.
-        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
-        * A template is injected using the template manager in the correct position.
-        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
-        *
-        * For further information please refer to the documentation page, https://doc.babylonjs.com
-        */
-    export class Template {
-            name: string;
-            /**
-                * Will be triggered when the template is loaded
-                */
-            onLoaded: BABYLON.Observable<Template>;
-            /**
-                * will be triggered when the template is appended to the tree
-                */
-            onAppended: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when the template's state changed (shown, hidden)
-                */
-            onStateChange: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when an event is triggered on ths template.
-                * The event is a native browser event (like mouse or pointer events)
-                */
-            onEventTriggered: BABYLON.Observable<EventCallback>;
-            onParamsUpdated: BABYLON.Observable<Template>;
-            onHTMLRendered: BABYLON.Observable<Template>;
-            /**
-                * is the template loaded?
-                */
-            isLoaded: boolean;
-            /**
-                * This is meant to be used to track the show and hide functions.
-                * This is NOT (!!) a flag to check if the element is actually visible to the user.
-                */
-            isShown: boolean;
-            /**
-                * Is this template a part of the HTML tree (the template manager injected it)
-                */
-            isInHtmlTree: boolean;
-            /**
-                * The HTML element containing this template
-                */
-            parent: HTMLElement;
-            /**
-                * A promise that is fulfilled when the template finished loading.
-                */
-            initPromise: Promise<Template>;
-            constructor(name: string, _configuration: ITemplateConfiguration);
-            /**
-                * Some templates have parameters (like background color for example).
-                * The parameters are provided to Handlebars which in turn generates the template.
-                * This function will update the template with the new parameters
-                *
-                * Note that when updating parameters the events will be registered again (after being cleared).
-                *
-                * @param params the new template parameters
-                */
-            updateParams(params: {
-                    [key: string]: string | number | boolean | object;
-            }, append?: boolean): void;
-            redraw(): void;
-            /**
-                * Get the template'S configuration
-                */
-            readonly configuration: ITemplateConfiguration;
-            /**
-                * A template can be a parent element for other templates or HTML elements.
-                * This function will deliver all child HTML elements of this template.
-                */
-            getChildElements(): Array<string>;
-            /**
-                * Appending the template to a parent HTML element.
-                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
-                * @param parent the parent to which the template is added
-                * @param forceRemove if the parent already exists, shoud the template be removed from it?
-                */
-            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
-            /**
-                * Show the template using the provided visibilityFunction, or natively using display: flex.
-                * The provided function returns a promise that should be fullfilled when the element is shown.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Hide the template using the provided visibilityFunction, or natively using display: none.
-                * The provided function returns a promise that should be fullfilled when the element is hidden.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Dispose this template
-                */
-            dispose(): void;
-    }
-}
-declare module BabylonViewer {
     export class ConfigurationContainer {
         configuration: ViewerConfiguration;
         viewerId: string;
@@ -1479,6 +1240,171 @@ declare module BabylonViewer {
     }
 }
 declare module BabylonViewer {
+    /**
+        * The object sent when an event is triggered
+        */
+    export interface EventCallback {
+            event: Event;
+            template: Template;
+            selector: string;
+            payload?: any;
+    }
+    /**
+        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
+        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
+        */
+    export class TemplateManager {
+            containerElement: Element;
+            /**
+                * Will be triggered when any template is initialized
+                */
+            onTemplateInit: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when any template is fully loaded
+                */
+            onTemplateLoaded: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when a template state changes
+                */
+            onTemplateStateChange: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when all templates finished loading
+                */
+            onAllLoaded: BABYLON.Observable<TemplateManager>;
+            /**
+                * Will be triggered when any event on any template is triggered.
+                */
+            onEventTriggered: BABYLON.Observable<EventCallback>;
+            /**
+                * This template manager's event manager. In charge of callback registrations to native event types
+                */
+            eventManager: EventManager;
+            constructor(containerElement: Element);
+            /**
+                * Initialize the template(s) for the viewer. Called bay the Viewer class
+                * @param templates the templates to be used to initialize the main template
+                */
+            initTemplate(templates: {
+                    [key: string]: ITemplateConfiguration;
+            }): Promise<void>;
+            /**
+                * Get the canvas in the template tree.
+                * There must be one and only one canvas inthe template.
+                */
+            getCanvas(): HTMLCanvasElement | null;
+            /**
+                * Get a specific template from the template tree
+                * @param name the name of the template to load
+                */
+            getTemplate(name: string): Template | undefined;
+            /**
+                * Dispose the template manager
+                */
+            dispose(): void;
+    }
+    /**
+        * This class represents a single template in the viewer's template tree.
+        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
+        * A template is injected using the template manager in the correct position.
+        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
+        *
+        * For further information please refer to the documentation page, https://doc.babylonjs.com
+        */
+    export class Template {
+            name: string;
+            /**
+                * Will be triggered when the template is loaded
+                */
+            onLoaded: BABYLON.Observable<Template>;
+            /**
+                * will be triggered when the template is appended to the tree
+                */
+            onAppended: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when the template's state changed (shown, hidden)
+                */
+            onStateChange: BABYLON.Observable<Template>;
+            /**
+                * Will be triggered when an event is triggered on ths template.
+                * The event is a native browser event (like mouse or pointer events)
+                */
+            onEventTriggered: BABYLON.Observable<EventCallback>;
+            onParamsUpdated: BABYLON.Observable<Template>;
+            onHTMLRendered: BABYLON.Observable<Template>;
+            /**
+                * is the template loaded?
+                */
+            isLoaded: boolean;
+            /**
+                * This is meant to be used to track the show and hide functions.
+                * This is NOT (!!) a flag to check if the element is actually visible to the user.
+                */
+            isShown: boolean;
+            /**
+                * Is this template a part of the HTML tree (the template manager injected it)
+                */
+            isInHtmlTree: boolean;
+            /**
+                * The HTML element containing this template
+                */
+            parent: HTMLElement;
+            /**
+                * A promise that is fulfilled when the template finished loading.
+                */
+            initPromise: Promise<Template>;
+            constructor(name: string, _configuration: ITemplateConfiguration);
+            /**
+                * Some templates have parameters (like background color for example).
+                * The parameters are provided to Handlebars which in turn generates the template.
+                * This function will update the template with the new parameters
+                *
+                * Note that when updating parameters the events will be registered again (after being cleared).
+                *
+                * @param params the new template parameters
+                */
+            updateParams(params: {
+                    [key: string]: string | number | boolean | object;
+            }, append?: boolean): void;
+            redraw(): void;
+            /**
+                * Get the template'S configuration
+                */
+            readonly configuration: ITemplateConfiguration;
+            /**
+                * A template can be a parent element for other templates or HTML elements.
+                * This function will deliver all child HTML elements of this template.
+                */
+            getChildElements(): Array<string>;
+            /**
+                * Appending the template to a parent HTML element.
+                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
+                * @param parent the parent to which the template is added
+                * @param forceRemove if the parent already exists, shoud the template be removed from it?
+                */
+            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
+            /**
+                * Show the template using the provided visibilityFunction, or natively using display: flex.
+                * The provided function returns a promise that should be fullfilled when the element is shown.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Hide the template using the provided visibilityFunction, or natively using display: none.
+                * The provided function returns a promise that should be fullfilled when the element is hidden.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Dispose this template
+                */
+            dispose(): void;
+    }
+}
+declare module BabylonViewer {
     export interface IModelConfiguration {
             id?: string;
             url?: string;
@@ -1599,39 +1525,6 @@ declare module BabylonViewer {
 }
 declare module BabylonViewer {
     /**
-        * The EventManager is in charge of registering user interctions with the viewer.
-        * It is used in the TemplateManager
-        */
-    export class EventManager {
-            constructor(_templateManager: TemplateManager);
-            /**
-                * Register a new callback to a specific template.
-                * The best example for the usage can be found in the DefaultViewer
-                *
-                * @param templateName the templateName to register the event to
-                * @param callback The callback to be executed
-                * @param eventType the type of event to register
-                * @param selector an optional selector. if not defined the parent object in the template will be selected
-                */
-            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * This will remove a registered event from the defined template.
-                * Each one of the variables apart from the template name are optional, but one must be provided.
-                *
-                * @param templateName the templateName
-                * @param callback the callback to remove (optional)
-                * @param eventType the event type to remove (optional)
-                * @param selector the selector from which to remove the event (optional)
-                */
-            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * Dispose the event manager
-                */
-            dispose(): void;
-    }
-}
-declare module BabylonViewer {
-    /**
         * The ViewerLabs class will hold functions that are not (!) backwards compatible.
         * The APIs in all labs-related classes and configuration  might change.
         * Once stable, lab features will be moved to the publis API and configuration object.
@@ -1680,6 +1573,39 @@ declare module BabylonViewer {
 }
 declare module BabylonViewer {
     /**
+        * The EventManager is in charge of registering user interctions with the viewer.
+        * It is used in the TemplateManager
+        */
+    export class EventManager {
+            constructor(_templateManager: TemplateManager);
+            /**
+                * Register a new callback to a specific template.
+                * The best example for the usage can be found in the DefaultViewer
+                *
+                * @param templateName the templateName to register the event to
+                * @param callback The callback to be executed
+                * @param eventType the type of event to register
+                * @param selector an optional selector. if not defined the parent object in the template will be selected
+                */
+            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * This will remove a registered event from the defined template.
+                * Each one of the variables apart from the template name are optional, but one must be provided.
+                *
+                * @param templateName the templateName
+                * @param callback the callback to remove (optional)
+                * @param eventType the event type to remove (optional)
+                * @param selector the selector from which to remove the event (optional)
+                */
+            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * Dispose the event manager
+                */
+            dispose(): void;
+    }
+}
+declare module BabylonViewer {
+    /**
         * Defines an animation to be applied to a model (translation, scale or rotation).
         */
     export interface IModelAnimationConfiguration {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -150,85 +150,7 @@ declare module 'babylonjs-viewer/viewer/viewerManager' {
 }
 
 declare module 'babylonjs-viewer/viewer/defaultViewer' {
-    import { ViewerConfiguration, IModelConfiguration } from 'babylonjs-viewer/configuration';
-    import { Template } from 'babylonjs-viewer/templating/templateManager';
-    import { AbstractViewer } from 'babylonjs-viewer/viewer/viewer';
-    import { ViewerModel } from 'babylonjs-viewer/model/viewerModel';
-    import { IViewerTemplatePlugin } from 'babylonjs-viewer/templating/viewerTemplatePlugin';
-    /**
-        * The Default viewer is the default implementation of the AbstractViewer.
-        * It uses the templating system to render a new canvas and controls.
-        */
-    export class DefaultViewer extends AbstractViewer {
-            containerElement: Element;
-            fullscreenElement?: Element;
-            /**
-                * Create a new default viewer
-                * @param containerElement the element in which the templates will be rendered
-                * @param initialConfiguration the initial configuration. Defaults to extending the default configuration
-                */
-            constructor(containerElement: Element, initialConfiguration?: ViewerConfiguration);
-            registerTemplatePlugin(plugin: IViewerTemplatePlugin): void;
-            /**
-                * This will be executed when the templates initialize.
-                */
-            protected _onTemplatesLoaded(): Promise<AbstractViewer>;
-            protected _initVR(): void;
-            /**
-                * Toggle fullscreen of the entire viewer
-                */
-            toggleFullscreen: () => void;
-            /**
-                * Preparing the container element to present the viewer
-                */
-            protected _prepareContainerElement(): void;
-            /**
-                * This function will configure the templates and update them after a model was loaded
-                * It is mainly responsible to changing the title and subtitle etc'.
-                * @param model the model to be used to configure the templates by
-                */
-            protected _configureTemplate(model?: ViewerModel): void;
-            /**
-                * This will load a new model to the default viewer
-                * overriding the AbstractViewer's loadModel.
-                * The scene will automatically be cleared of the old models, if exist.
-                * @param model the configuration object (or URL) to load.
-                */
-            loadModel(model?: string | File | IModelConfiguration): Promise<ViewerModel>;
-            /**
-                * Show the overlay and the defined sub-screen.
-                * Mainly used for help and errors
-                * @param subScreen the name of the subScreen. Those can be defined in the configuration object
-                */
-            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
-            /**
-                * Hide the overlay screen.
-                */
-            hideOverlayScreen(): Promise<string> | Promise<Template>;
-            /**
-                * show the viewer (in case it was hidden)
-                *
-                * @param visibilityFunction an optional function to execute in order to show the container
-                */
-            show(visibilityFunction?: ((template: Template) => Promise<Template>)): Promise<Template>;
-            /**
-                * hide the viewer (in case it is visible)
-                *
-                * @param visibilityFunction an optional function to execute in order to hide the container
-                */
-            hide(visibilityFunction?: ((template: Template) => Promise<Template>)): Promise<Template>;
-            /**
-                * Show the loading screen.
-                * The loading screen can be configured using the configuration object
-                */
-            showLoadingScreen(): Promise<string> | Promise<Template>;
-            /**
-                * Hide the loading screen
-                */
-            hideLoadingScreen(): Promise<string> | Promise<Template>;
-            dispose(): void;
-            protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
-    }
+    
 }
 
 declare module 'babylonjs-viewer/viewer/viewer' {
@@ -1114,175 +1036,6 @@ declare module 'babylonjs-viewer/configuration/configuration' {
     }
 }
 
-declare module 'babylonjs-viewer/templating/templateManager' {
-    import { Observable } from 'babylonjs';
-    import { EventManager } from 'babylonjs-viewer/templating/eventManager';
-    import { ITemplateConfiguration } from 'babylonjs-viewer/configuration/interfaces';
-    /**
-        * The object sent when an event is triggered
-        */
-    export interface EventCallback {
-            event: Event;
-            template: Template;
-            selector: string;
-            payload?: any;
-    }
-    /**
-        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
-        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
-        */
-    export class TemplateManager {
-            containerElement: Element;
-            /**
-                * Will be triggered when any template is initialized
-                */
-            onTemplateInit: Observable<Template>;
-            /**
-                * Will be triggered when any template is fully loaded
-                */
-            onTemplateLoaded: Observable<Template>;
-            /**
-                * Will be triggered when a template state changes
-                */
-            onTemplateStateChange: Observable<Template>;
-            /**
-                * Will be triggered when all templates finished loading
-                */
-            onAllLoaded: Observable<TemplateManager>;
-            /**
-                * Will be triggered when any event on any template is triggered.
-                */
-            onEventTriggered: Observable<EventCallback>;
-            /**
-                * This template manager's event manager. In charge of callback registrations to native event types
-                */
-            eventManager: EventManager;
-            constructor(containerElement: Element);
-            /**
-                * Initialize the template(s) for the viewer. Called bay the Viewer class
-                * @param templates the templates to be used to initialize the main template
-                */
-            initTemplate(templates: {
-                    [key: string]: ITemplateConfiguration;
-            }): Promise<void>;
-            /**
-                * Get the canvas in the template tree.
-                * There must be one and only one canvas inthe template.
-                */
-            getCanvas(): HTMLCanvasElement | null;
-            /**
-                * Get a specific template from the template tree
-                * @param name the name of the template to load
-                */
-            getTemplate(name: string): Template | undefined;
-            /**
-                * Dispose the template manager
-                */
-            dispose(): void;
-    }
-    /**
-        * This class represents a single template in the viewer's template tree.
-        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
-        * A template is injected using the template manager in the correct position.
-        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
-        *
-        * For further information please refer to the documentation page, https://doc.babylonjs.com
-        */
-    export class Template {
-            name: string;
-            /**
-                * Will be triggered when the template is loaded
-                */
-            onLoaded: Observable<Template>;
-            /**
-                * will be triggered when the template is appended to the tree
-                */
-            onAppended: Observable<Template>;
-            /**
-                * Will be triggered when the template's state changed (shown, hidden)
-                */
-            onStateChange: Observable<Template>;
-            /**
-                * Will be triggered when an event is triggered on ths template.
-                * The event is a native browser event (like mouse or pointer events)
-                */
-            onEventTriggered: Observable<EventCallback>;
-            onParamsUpdated: Observable<Template>;
-            onHTMLRendered: Observable<Template>;
-            /**
-                * is the template loaded?
-                */
-            isLoaded: boolean;
-            /**
-                * This is meant to be used to track the show and hide functions.
-                * This is NOT (!!) a flag to check if the element is actually visible to the user.
-                */
-            isShown: boolean;
-            /**
-                * Is this template a part of the HTML tree (the template manager injected it)
-                */
-            isInHtmlTree: boolean;
-            /**
-                * The HTML element containing this template
-                */
-            parent: HTMLElement;
-            /**
-                * A promise that is fulfilled when the template finished loading.
-                */
-            initPromise: Promise<Template>;
-            constructor(name: string, _configuration: ITemplateConfiguration);
-            /**
-                * Some templates have parameters (like background color for example).
-                * The parameters are provided to Handlebars which in turn generates the template.
-                * This function will update the template with the new parameters
-                *
-                * Note that when updating parameters the events will be registered again (after being cleared).
-                *
-                * @param params the new template parameters
-                */
-            updateParams(params: {
-                    [key: string]: string | number | boolean | object;
-            }, append?: boolean): void;
-            redraw(): void;
-            /**
-                * Get the template'S configuration
-                */
-            readonly configuration: ITemplateConfiguration;
-            /**
-                * A template can be a parent element for other templates or HTML elements.
-                * This function will deliver all child HTML elements of this template.
-                */
-            getChildElements(): Array<string>;
-            /**
-                * Appending the template to a parent HTML element.
-                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
-                * @param parent the parent to which the template is added
-                * @param forceRemove if the parent already exists, shoud the template be removed from it?
-                */
-            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
-            /**
-                * Show the template using the provided visibilityFunction, or natively using display: flex.
-                * The provided function returns a promise that should be fullfilled when the element is shown.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Hide the template using the provided visibilityFunction, or natively using display: none.
-                * The provided function returns a promise that should be fullfilled when the element is hidden.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Dispose this template
-                */
-            dispose(): void;
-    }
-}
-
 declare module 'babylonjs-viewer/configuration/configurationContainer' {
     import { ViewerConfiguration } from 'babylonjs-viewer/configuration/configuration';
     import { Color3, Scene } from 'babylonjs';
@@ -1575,6 +1328,175 @@ declare module 'babylonjs-viewer/managers/sceneManager' {
     }
 }
 
+declare module 'babylonjs-viewer/templating/templateManager' {
+    import { Observable } from 'babylonjs';
+    import { EventManager } from 'babylonjs-viewer/templating/eventManager';
+    import { ITemplateConfiguration } from 'babylonjs-viewer/configuration/interfaces';
+    /**
+        * The object sent when an event is triggered
+        */
+    export interface EventCallback {
+            event: Event;
+            template: Template;
+            selector: string;
+            payload?: any;
+    }
+    /**
+        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
+        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
+        */
+    export class TemplateManager {
+            containerElement: Element;
+            /**
+                * Will be triggered when any template is initialized
+                */
+            onTemplateInit: Observable<Template>;
+            /**
+                * Will be triggered when any template is fully loaded
+                */
+            onTemplateLoaded: Observable<Template>;
+            /**
+                * Will be triggered when a template state changes
+                */
+            onTemplateStateChange: Observable<Template>;
+            /**
+                * Will be triggered when all templates finished loading
+                */
+            onAllLoaded: Observable<TemplateManager>;
+            /**
+                * Will be triggered when any event on any template is triggered.
+                */
+            onEventTriggered: Observable<EventCallback>;
+            /**
+                * This template manager's event manager. In charge of callback registrations to native event types
+                */
+            eventManager: EventManager;
+            constructor(containerElement: Element);
+            /**
+                * Initialize the template(s) for the viewer. Called bay the Viewer class
+                * @param templates the templates to be used to initialize the main template
+                */
+            initTemplate(templates: {
+                    [key: string]: ITemplateConfiguration;
+            }): Promise<void>;
+            /**
+                * Get the canvas in the template tree.
+                * There must be one and only one canvas inthe template.
+                */
+            getCanvas(): HTMLCanvasElement | null;
+            /**
+                * Get a specific template from the template tree
+                * @param name the name of the template to load
+                */
+            getTemplate(name: string): Template | undefined;
+            /**
+                * Dispose the template manager
+                */
+            dispose(): void;
+    }
+    /**
+        * This class represents a single template in the viewer's template tree.
+        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
+        * A template is injected using the template manager in the correct position.
+        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
+        *
+        * For further information please refer to the documentation page, https://doc.babylonjs.com
+        */
+    export class Template {
+            name: string;
+            /**
+                * Will be triggered when the template is loaded
+                */
+            onLoaded: Observable<Template>;
+            /**
+                * will be triggered when the template is appended to the tree
+                */
+            onAppended: Observable<Template>;
+            /**
+                * Will be triggered when the template's state changed (shown, hidden)
+                */
+            onStateChange: Observable<Template>;
+            /**
+                * Will be triggered when an event is triggered on ths template.
+                * The event is a native browser event (like mouse or pointer events)
+                */
+            onEventTriggered: Observable<EventCallback>;
+            onParamsUpdated: Observable<Template>;
+            onHTMLRendered: Observable<Template>;
+            /**
+                * is the template loaded?
+                */
+            isLoaded: boolean;
+            /**
+                * This is meant to be used to track the show and hide functions.
+                * This is NOT (!!) a flag to check if the element is actually visible to the user.
+                */
+            isShown: boolean;
+            /**
+                * Is this template a part of the HTML tree (the template manager injected it)
+                */
+            isInHtmlTree: boolean;
+            /**
+                * The HTML element containing this template
+                */
+            parent: HTMLElement;
+            /**
+                * A promise that is fulfilled when the template finished loading.
+                */
+            initPromise: Promise<Template>;
+            constructor(name: string, _configuration: ITemplateConfiguration);
+            /**
+                * Some templates have parameters (like background color for example).
+                * The parameters are provided to Handlebars which in turn generates the template.
+                * This function will update the template with the new parameters
+                *
+                * Note that when updating parameters the events will be registered again (after being cleared).
+                *
+                * @param params the new template parameters
+                */
+            updateParams(params: {
+                    [key: string]: string | number | boolean | object;
+            }, append?: boolean): void;
+            redraw(): void;
+            /**
+                * Get the template'S configuration
+                */
+            readonly configuration: ITemplateConfiguration;
+            /**
+                * A template can be a parent element for other templates or HTML elements.
+                * This function will deliver all child HTML elements of this template.
+                */
+            getChildElements(): Array<string>;
+            /**
+                * Appending the template to a parent HTML element.
+                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
+                * @param parent the parent to which the template is added
+                * @param forceRemove if the parent already exists, shoud the template be removed from it?
+                */
+            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
+            /**
+                * Show the template using the provided visibilityFunction, or natively using display: flex.
+                * The provided function returns a promise that should be fullfilled when the element is shown.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Hide the template using the provided visibilityFunction, or natively using display: none.
+                * The provided function returns a promise that should be fullfilled when the element is hidden.
+                * Since it is a promise async operations are more than possible.
+                * See the default viewer for an opacity example.
+                * @param visibilityFunction The function to execute to show the template.
+                */
+            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+            /**
+                * Dispose this template
+                */
+            dispose(): void;
+    }
+}
+
 declare module 'babylonjs-viewer/configuration/interfaces/modelConfiguration' {
     import { IModelAnimationConfiguration } from "babylonjs-viewer/configuration/interfaces/modelAnimationConfiguration";
     export interface IModelConfiguration {
@@ -1722,41 +1644,6 @@ declare module 'babylonjs-viewer/configuration/interfaces/environmentMapConfigur
     }
 }
 
-declare module 'babylonjs-viewer/templating/eventManager' {
-    import { EventCallback, TemplateManager } from "babylonjs-viewer/templating/templateManager";
-    /**
-        * The EventManager is in charge of registering user interctions with the viewer.
-        * It is used in the TemplateManager
-        */
-    export class EventManager {
-            constructor(_templateManager: TemplateManager);
-            /**
-                * Register a new callback to a specific template.
-                * The best example for the usage can be found in the DefaultViewer
-                *
-                * @param templateName the templateName to register the event to
-                * @param callback The callback to be executed
-                * @param eventType the type of event to register
-                * @param selector an optional selector. if not defined the parent object in the template will be selected
-                */
-            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * This will remove a registered event from the defined template.
-                * Each one of the variables apart from the template name are optional, but one must be provided.
-                *
-                * @param templateName the templateName
-                * @param callback the callback to remove (optional)
-                * @param eventType the event type to remove (optional)
-                * @param selector the selector from which to remove the event (optional)
-                */
-            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * Dispose the event manager
-                */
-            dispose(): void;
-    }
-}
-
 declare module 'babylonjs-viewer/labs/viewerLabs' {
     import { PBREnvironment } from "babylonjs-viewer/labs/environmentSerializer";
     import { ShadowLight, Vector3, Scene } from 'babylonjs';
@@ -1808,6 +1695,41 @@ declare module 'babylonjs-viewer/labs/viewerLabs' {
     }
 }
 
+declare module 'babylonjs-viewer/templating/eventManager' {
+    import { EventCallback, TemplateManager } from "babylonjs-viewer/templating/templateManager";
+    /**
+        * The EventManager is in charge of registering user interctions with the viewer.
+        * It is used in the TemplateManager
+        */
+    export class EventManager {
+            constructor(_templateManager: TemplateManager);
+            /**
+                * Register a new callback to a specific template.
+                * The best example for the usage can be found in the DefaultViewer
+                *
+                * @param templateName the templateName to register the event to
+                * @param callback The callback to be executed
+                * @param eventType the type of event to register
+                * @param selector an optional selector. if not defined the parent object in the template will be selected
+                */
+            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * This will remove a registered event from the defined template.
+                * Each one of the variables apart from the template name are optional, but one must be provided.
+                *
+                * @param templateName the templateName
+                * @param callback the callback to remove (optional)
+                * @param eventType the event type to remove (optional)
+                * @param selector the selector from which to remove the event (optional)
+                */
+            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+            /**
+                * Dispose the event manager
+                */
+            dispose(): void;
+    }
+}
+
 declare module 'babylonjs-viewer/configuration/interfaces/modelAnimationConfiguration' {
     /**
         * Defines an animation to be applied to a model (translation, scale or rotation).

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

@@ -9,9 +9,12 @@
   - 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))
   - 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 camera/engine ([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))
+  - WebXRInput manage controllers for the XR experience ([TrevorDev](https://github.com/TrevorDev))
+- GUI:
+  - Added new [ImageBasedSlider](http://doc.babylonjs.com/how_to/gui#imagebasedslider) to let users customize sliders using images ([Deltakosh](https://github.com/deltakosh))
 
 ## Updates
 

+ 20 - 3
gui/src/2D/controls/baseSlider.ts

@@ -13,6 +13,7 @@ export class BaseSlider extends Control {
     private _isVertical = false;
     protected _barOffset = new ValueAndUnit(5, ValueAndUnit.UNITMODE_PIXEL, false);
     private _isThumbClamped = false;
+    protected _displayThumb = true;
 
     // Shared rendering info
     protected _effectiveBarOffset = 0;
@@ -27,6 +28,20 @@ export class BaseSlider extends Control {
     /** Observable raised when the sldier value changes */
     public onValueChangedObservable = new Observable<number>();
 
+    /** Gets or sets a boolean indicating if the thumb must be rendered */
+    public get displayThumb(): boolean {
+        return this._displayThumb;
+    }
+
+    public set displayThumb(value: boolean) {
+        if (this._displayThumb === value) {
+            return;
+        }
+
+        this._displayThumb = value;
+        this._markAsDirty();
+    }
+
     /** Gets or sets main bar offset (ie. the margin applied to the value bar) */
     public get barOffset(): string | number {
         return this._barOffset.toString(this._host);
@@ -200,7 +215,9 @@ export class BaseSlider extends Control {
         this._backgroundBoxThickness = Math.min(this._currentMeasure.width, this._currentMeasure.height);
         this._effectiveThumbThickness = this._getThumbThickness(type);
 
-        this._backgroundBoxLength -= this._effectiveThumbThickness;
+        if (this.displayThumb) {
+            this._backgroundBoxLength -= this._effectiveThumbThickness;
+        }
         //throw error when height is less than width for vertical slider
         if ((this.isVertical && this._currentMeasure.height < this._currentMeasure.width)) {
             console.error("Height should be greater than width");
@@ -217,7 +234,7 @@ export class BaseSlider extends Control {
 
         if (this.isVertical) {
             this._renderLeft += this._effectiveBarOffset;
-            if (!this.isThumbClamped) {
+            if (!this.isThumbClamped && this.displayThumb) {
                 this._renderTop += (this._effectiveThumbThickness / 2);
             }
 
@@ -227,7 +244,7 @@ export class BaseSlider extends Control {
         }
         else {
             this._renderTop += this._effectiveBarOffset;
-            if (!this.isThumbClamped) {
+            if (!this.isThumbClamped && this.displayThumb) {
                 this._renderLeft += (this._effectiveThumbThickness / 2);
             }
             this._renderHeight = this._backgroundBoxThickness;

+ 23 - 7
gui/src/2D/controls/imageBasedSlider.ts

@@ -12,6 +12,19 @@ export class ImageBasedSlider extends BaseSlider {
 
     private _tempMeasure = new Measure(0, 0, 0, 0);
 
+    public get displayThumb(): boolean {
+        return this._displayThumb && this.thumbImage != null;
+    }
+
+    public set displayThumb(value: boolean) {
+        if (this._displayThumb === value) {
+            return;
+        }
+
+        this._displayThumb = value;
+        this._markAsDirty();
+    }
+
     /**
      * Gets or sets the image used to render the background
      */
@@ -103,13 +116,12 @@ export class ImageBasedSlider extends BaseSlider {
             // Background
             if (this._backgroundImage) {
                 this._tempMeasure.copyFromFloats(left, top, width, height);
-                if (this.isThumbClamped) {
+                if (this.isThumbClamped && this.displayThumb) {
                     if (this.isVertical) {
                         this._tempMeasure.height += this._effectiveThumbThickness;
                     } else {
                         this._tempMeasure.width += this._effectiveThumbThickness;
                     }
-
                 }
                 this._backgroundImage._draw(this._tempMeasure, context);
             }
@@ -117,20 +129,24 @@ export class ImageBasedSlider extends BaseSlider {
             // Bar
             if (this._valueBarImage) {
                 if (this.isVertical) {
-                    this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition);
-                    if (this.isThumbClamped) {
-                        this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, this._currentMeasure.height - thumbPosition);
+                    if (this.isThumbClamped && this.displayThumb) {
+                        this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
                     } else {
                         this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition);
                     }
                 } else {
-                    this._tempMeasure.copyFromFloats(left, top, thumbPosition + this._effectiveThumbThickness / 2, height);
+                    if (this.isThumbClamped && this.displayThumb) {
+                        this._tempMeasure.copyFromFloats(left, top, thumbPosition + this._effectiveThumbThickness / 2, height);
+                    }
+                    else {
+                        this._tempMeasure.copyFromFloats(left, top, thumbPosition, height);
+                    }
                 }
                 this._valueBarImage._draw(this._tempMeasure, context);
             }
 
             // Thumb
-            if (this._thumbImage) {
+            if (this.displayThumb) {
                 if (this.isVertical) {
                     this._tempMeasure.copyFromFloats(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
                 } else {

+ 1 - 16
gui/src/2D/controls/slider.ts

@@ -8,21 +8,6 @@ export class Slider extends BaseSlider {
     private _background = "black";
     private _borderColor = "white";
     private _isThumbCircle = false;
-    private _displayThumb = true;
-
-    /** Gets or sets a boolean indicating if the thumb must be rendered */
-    public get displayThumb(): boolean {
-        return this._displayThumb;
-    }
-
-    public set displayThumb(value: boolean) {
-        if (this._displayThumb === value) {
-            return;
-        }
-
-        this._displayThumb = value;
-        this._markAsDirty();
-    }
 
     /** Gets or sets border color */
     public get borderColor(): string {
@@ -166,7 +151,7 @@ export class Slider extends BaseSlider {
                         context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
                     }
                     else {
-                        context.fillRect(left, top + thumbPosition, width, this._currentMeasure.height - thumbPosition);
+                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
                     }
                 }
                 else {

+ 4 - 0
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -1037,6 +1037,10 @@ module BABYLON.GLTF2 {
         }
 
         private _loadAnimationChannelAsync(context: string, animationContext: string, animation: Loader.IAnimation, channel: Loader.IAnimationChannel, babylonAnimationGroup: AnimationGroup): Promise<void> {
+            if (channel.target.node == undefined) {
+                return Promise.resolve();
+            }
+
             const targetNode = ArrayItem.Get(`${context}/target/node`, this.gltf.nodes, channel.target.node);
 
             // Ignore animations that have no animation targets.

+ 8 - 5
sandbox/index.js

@@ -119,11 +119,14 @@ if (BABYLON.Engine.isSupported()) {
 
         // Sync the slider with the current frame
         babylonScene.registerBeforeRender(function() {
-            if (currentGroup != null && currentGroup.targetedAnimations[0].animation.runtimeAnimations[0] != null) {
-                var currentValue = slider.valueAsNumber;
-                var newValue = currentGroup.targetedAnimations[0].animation.runtimeAnimations[0].currentFrame;
-                var range = Math.abs(currentGroup.from - currentGroup.to);
-                slider.value = newValue;
+            if (currentGroup) {
+                var targetedAnimations = currentGroup.targetedAnimations;
+                if (targetedAnimations.length > 0) {
+                    var runtimeAnimations = currentGroup.targetedAnimations[0].animation.runtimeAnimations;
+                    if (runtimeAnimations.length > 0) {
+                        slider.value = runtimeAnimations[0].currentFrame;
+                    }
+                }
             }
         });
 

+ 55 - 7
src/Cameras/XR/babylon.webXREnterExitUI.ts

@@ -1,5 +1,28 @@
 module BABYLON {
     /**
+     * Button which can be used to enter a different mode of XR
+     */
+    export class WebXREnterExitUIButton {
+        /**
+         * Creates a WebXREnterExitUIButton
+         * @param element button element
+         * @param initializationOptions XR initialization options for the button
+         */
+        constructor(
+            /** button element */
+            public element: HTMLElement,
+            /** XR initialization options for the button */
+            public initializationOptions: XRSessionCreationOptions
+        ) {}
+        /**
+         * Overwritable function which can be used to update the button's visuals when the state changes
+         * @param activeButton the current active button in the UI
+         */
+        update(activeButton: Nullable<WebXREnterExitUIButton>) {
+        }
+    }
+
+    /**
      * Options to create the webXR UI
      */
     export class WebXREnterExitUIOptions {
@@ -11,14 +34,15 @@ module BABYLON {
         /**
          * User provided buttons to enable/disable WebXR. The system will provide default if not set
          */
-        customButtons?: Array<{ element: HTMLElement, initializationOptions: XRSessionCreationOptions }>;
+        customButtons?: Array<WebXREnterExitUIButton>;
     }
     /**
      * 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 }> = [];
+        private _buttons: Array<WebXREnterExitUIButton> = [];
+        private _activeButton: Nullable<WebXREnterExitUIButton> = null;
         /**
          * Creates UI to allow the user to enter/exit XR mode
          * @param scene the scene to add the ui to
@@ -29,7 +53,12 @@ module BABYLON {
         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 helper.supportsSessionAsync(btn.initializationOptions);
+            });
+            helper.onStateChangedObservable.add((state) => {
+                if (state == WebXRState.NOT_IN_XR) {
+                    ui._updateButtons(null);
+                }
             });
             return Promise.all(supportedPromises).then((results) => {
                 results.forEach((supported, i) => {
@@ -37,10 +66,12 @@ module BABYLON {
                         ui._overlay.appendChild(ui._buttons[i].element);
                         ui._buttons[i].element.onclick = async() => {
                             if (helper.state == BABYLON.WebXRState.IN_XR) {
-                                await helper.exitXR();
+                                ui._updateButtons(null);
+                                await helper.exitXRAsync();
                                 return;
                             } else if (helper.state == BABYLON.WebXRState.NOT_IN_XR) {
-                                await helper.enterXR(ui._buttons[i].initializationOptions, "eye-level");
+                                ui._updateButtons(ui._buttons[i]);
+                                await helper.enterXRAsync(ui._buttons[i].initializationOptions, "eye-level");
                             }
                         };
                     }
@@ -58,12 +89,21 @@ module BABYLON {
                 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 } });
+                this._buttons.push(new WebXREnterExitUIButton(hmdBtn, {immersive: true, outputContext: options.outputCanvasContext}));
+                this._buttons[this._buttons.length - 1].update = function(activeButton: WebXREnterExitUIButton) {
+                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                    this.element.innerText = activeButton === this ? "EXIT" : "HMD";
+                };
 
                 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 } });
+                this._buttons.push(new WebXREnterExitUIButton(windowBtn, { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext }));
+                this._buttons[this._buttons.length - 1].update = function(activeButton: WebXREnterExitUIButton) {
+                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                    this.element.innerText = activeButton === this ? "EXIT" : "Window";
+                };
+                this._updateButtons(null);
             }
 
             var renderCanvas = scene.getEngine().getRenderingCanvas();
@@ -74,6 +114,14 @@ module BABYLON {
                 });
             }
         }
+
+        private _updateButtons(activeButton: Nullable<WebXREnterExitUIButton>) {
+            this._activeButton = activeButton;
+            this._buttons.forEach((b) => {
+                b.update(this._activeButton);
+            });
+        }
+
         /**
          * Disposes of the object
          */

+ 18 - 8
src/Cameras/XR/babylon.webXRExperienceHelper.ts

@@ -49,7 +49,8 @@ module BABYLON {
          */
         public onStateChangedObservable = new Observable<WebXRState>();
 
-        private _sessionManager: WebXRSessionManager;
+        /** @hidden */
+        public _sessionManager: WebXRSessionManager;
 
         private _nonVRCamera: Nullable<Camera> = null;
         private _originalSceneAutoClear = true;
@@ -63,7 +64,7 @@ module BABYLON {
          */
         public static CreateAsync(scene: BABYLON.Scene): Promise<WebXRExperienceHelper> {
             var helper = new WebXRExperienceHelper(scene);
-            return helper._sessionManager.initialize().then(() => {
+            return helper._sessionManager.initializeAsync().then(() => {
                 helper._supported = true;
                 return helper;
             }).catch(() => {
@@ -85,9 +86,9 @@ module BABYLON {
          * Exits XR mode and returns the scene to its original state
          * @returns promise that resolves after xr mode has exited
          */
-        public exitXR() {
+        public exitXRAsync() {
             this._setState(WebXRState.EXITING_XR);
-            return this._sessionManager.exitXR();
+            return this._sessionManager.exitXRAsync();
         }
 
         /**
@@ -96,10 +97,10 @@ module BABYLON {
          * @param frameOfReference frame of reference of the XR session
          * @returns promise that resolves after xr mode has entered
          */
-        public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
+        public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
             this._setState(WebXRState.ENTERING_XR);
 
-            return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(() => {
+            return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(() => {
                 // Cache pre xr scene settings
                 this._originalSceneAutoClear = this.scene.autoClear;
                 this._nonVRCamera = this.scene.activeCamera;
@@ -130,15 +131,24 @@ module BABYLON {
         }
 
         /**
+         * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
+         * @param ray ray to cast into the environment
+         * @returns Promise which resolves with a collision point in the environment if it exists
+         */
+        public environmentPointHitTestAsync(ray: BABYLON.Ray): Promise<Nullable<Vector3>> {
+            return this._sessionManager.environmentPointHitTestAsync(ray);
+        }
+
+        /**
          * Checks if the creation options are supported by the xr session
          * @param options creation options
          * @returns true if supported
          */
-        public supportsSession(options: XRSessionCreationOptions) {
+        public supportsSessionAsync(options: XRSessionCreationOptions) {
             if (!this._supported) {
                 return Promise.resolve(false);
             }
-            return this._sessionManager.supportsSession(options);
+            return this._sessionManager.supportsSessionAsync(options);
         }
 
         /**

+ 104 - 0
src/Cameras/XR/babylon.webXRInput.ts

@@ -0,0 +1,104 @@
+module BABYLON {
+    /**
+     * Represents an XR input
+     */
+    export class WebXRController {
+        /**
+         * Represents the part of the controller that is held. This may not exist if the controller is the head mounted display itself, if thats the case only the pointer from the head will be availible
+         */
+        public grip?: BABYLON.AbstractMesh;
+        /**
+         * Pointer which can be used to select objects or attach a visible laser to
+         */
+        public pointer: BABYLON.AbstractMesh;
+
+        /**
+         * Creates the controller
+         * @see https://doc.babylonjs.com/how_to/webxr
+         * @param scene the scene which the controller should be associated to
+         */
+        constructor(scene: BABYLON.Scene) {
+            this.pointer = new BABYLON.AbstractMesh("controllerPointer", scene);
+        }
+        /**
+         * Disposes of the object
+         */
+        dispose() {
+            if (this.grip) {
+                this.grip.dispose();
+            }
+            this.pointer.dispose();
+        }
+    }
+
+    /**
+     * XR input used to track XR inputs such as controllers/rays
+     */
+    export class WebXRInput implements IDisposable {
+        /**
+         * XR controllers being tracked
+         */
+        public controllers: Array<WebXRController> = [];
+        private _tmpMatrix = new BABYLON.Matrix();
+        private _frameObserver: Nullable<Observer<any>>;
+
+        /**
+         * Initializes the WebXRInput
+         * @param helper experience helper which the input should be created for
+         */
+        public constructor(private helper: WebXRExperienceHelper) {
+            this._frameObserver = helper._sessionManager.onXRFrameObservable.add(() => {
+                if (!helper._sessionManager._currentXRFrame || !helper._sessionManager._currentXRFrame.getDevicePose) {
+                    return false;
+                }
+
+                var xrFrame = helper._sessionManager._currentXRFrame;
+                var inputSources = helper._sessionManager._xrSession.getInputSources();
+
+                inputSources.forEach((input, i) => {
+                    let inputPose = xrFrame.getInputPose(input, helper._sessionManager._frameOfReference);
+                    if (inputPose) {
+                        if (this.controllers.length <= i) {
+                            this.controllers.push(new WebXRController(helper.container.getScene()));
+                        }
+                        var controller = this.controllers[i];
+
+                        // Manage the grip if it exists
+                        if (inputPose.gripMatrix) {
+                            if (!controller.grip) {
+                                controller.grip = new BABYLON.AbstractMesh("controllerGrip", helper.container.getScene());
+                            }
+                            BABYLON.Matrix.FromFloat32ArrayToRefScaled(inputPose.gripMatrix, 0, 1, this._tmpMatrix);
+                            if (!controller.grip.getScene().useRightHandedSystem) {
+                                this._tmpMatrix.toggleModelMatrixHandInPlace();
+                            }
+                            if (!controller.grip.rotationQuaternion) {
+                                controller.grip.rotationQuaternion = new BABYLON.Quaternion();
+                            }
+                            this._tmpMatrix.decompose(controller.grip.scaling, controller.grip.rotationQuaternion, controller.grip.position);
+                        }
+
+                        // Manager pointer of controller
+                        BABYLON.Matrix.FromFloat32ArrayToRefScaled(inputPose.targetRay.transformMatrix, 0, 1, this._tmpMatrix);
+                        if (!controller.pointer.getScene().useRightHandedSystem) {
+                            this._tmpMatrix.toggleModelMatrixHandInPlace();
+                        }
+                        if (!controller.pointer.rotationQuaternion) {
+                            controller.pointer.rotationQuaternion = new BABYLON.Quaternion();
+                        }
+                        this._tmpMatrix.decompose(controller.pointer.scaling, controller.pointer.rotationQuaternion, controller.pointer.position);
+                    }
+                });
+            });
+        }
+        /**
+         * Disposes of the object
+         */
+        public dispose() {
+            this.controllers.forEach((c) => {
+                c.dispose();
+            });
+            this.helper._sessionManager.onXRFrameObservable.remove(this._frameObserver);
+        }
+    }
+}

+ 1 - 1
src/Cameras/XR/babylon.webXRManagedOutputCanvas.ts

@@ -16,7 +16,7 @@ module BABYLON {
         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;";
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #000000;";
             }
             this._setManagedOutputCanvas(canvas);
             helper.onStateChangedObservable.add((stateInfo) => {

+ 5 - 5
src/Cameras/XR/babylon.webXRSessionManager.ts

@@ -38,7 +38,7 @@ module BABYLON {
          * After initialization enterXR can be called to start an XR session
          * @returns Promise which resolves after it is initialized
          */
-        public initialize(): Promise<void> {
+        public initializeAsync(): Promise<void> {
              // Check if the browser supports webXR
             this._xrNavigator = navigator;
             if (!this._xrNavigator.xr) {
@@ -57,7 +57,7 @@ module BABYLON {
          * @param frameOfReferenceType option to configure how the xr pose is expressed
          * @returns Promise which resolves after it enters XR
          */
-        public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: string): Promise<void> {
+        public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: string): Promise<void> {
             // initialize session
             return this._xrDevice.requestSession(sessionCreationOptions).then((session: XRSession) => {
                 this._xrSession = session;
@@ -103,7 +103,7 @@ module BABYLON {
          * Stops the xrSession and restores the renderloop
          * @returns Promise which resolves after it exits XR
          */
-        public exitXR() {
+        public exitXRAsync() {
             return this._xrSession.end();
         }
 
@@ -112,7 +112,7 @@ module BABYLON {
          * @param ray ray to cast into the environment
          * @returns Promise which resolves with a collision point in the environment if it exists
          */
-        public environmentPointHitTest(ray: BABYLON.Ray): Promise<Nullable<Vector3>> {
+        public environmentPointHitTestAsync(ray: BABYLON.Ray): Promise<Nullable<Vector3>> {
             return new Promise((res, rej) => {
                 // Compute left handed inputs to request hit test
                 var origin = new Float32Array([ray.origin.x, ray.origin.y, ray.origin.z]);
@@ -146,7 +146,7 @@ module BABYLON {
          * @param options creation options to check if they are supported
          * @returns true if supported
          */
-        public supportsSession(options: XRSessionCreationOptions) {
+        public supportsSessionAsync(options: XRSessionCreationOptions) {
             return this._xrDevice.supportsSession(options).then(() => {
                 return true;
             }).catch((e) => {

+ 3 - 1
src/Helpers/babylon.sceneHelpers.ts

@@ -184,7 +184,9 @@ module BABYLON {
     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 BABYLON.WebXREnterExitUI.CreateAsync(this, helper, {outputCanvasContext: outputCanvas.canvasContext})
+            .then((ui) => {
+                new BABYLON.WebXRInput(helper);
                 return helper;
             });
         });

+ 6 - 1
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -865,7 +865,7 @@ module BABYLON {
             var engine = scene.getEngine();
             let material = subMesh.getMaterial();
 
-            if (!material) {
+            if (!material || subMesh.verticesCount === 0) {
                 return;
             }
 
@@ -912,6 +912,11 @@ module BABYLON {
 
                     if (skeleton.isUsingTextureForMatrices) {
                         const boneTexture = skeleton.getTransformMatrixTexture();
+
+                        if (!boneTexture) {
+                            return;
+                        }
+
                         this._effect.setTexture("boneSampler", boneTexture);
                         this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
                     } else {

+ 2 - 0
src/babylon.mixins.ts

@@ -184,6 +184,7 @@ interface XRDevice {
     supportsSession(options: XRSessionCreationOptions): Promise<void>;
 }
 interface XRSession {
+    getInputSources(): Array<any>;
     baseLayer: XRWebGLLayer;
     requestFrameOfReference(type: string): Promise<void>;
     requestHitTest(origin: Float32Array, direction: Float32Array, frameOfReference: any): any;
@@ -206,6 +207,7 @@ interface XRView {
 }
 interface XRFrame {
     getDevicePose: Function;
+    getInputPose: Function;
     views: Array<XRView>;
     baseLayer: XRLayer;
 }

BIN
tests/validation/ReferenceImages/Sliders.png


+ 6 - 0
tests/validation/config.json

@@ -2,6 +2,12 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
+      "title": "Sliders",
+      "playgroundId": "#HATGQZ#3",
+      "referenceImage": "Sliders.png",
+      "excludeFromAutomaticTesting": true
+    },
+    {
       "title": "Pointers",
       "playgroundId": "#1GLEJK#5",
       "referenceImage": "pointers.png",