瀏覽代碼

Merge pull request #7410 from BabylonJS/master

4.1.0-beta.20
David Catuhe 5 年之前
父節點
當前提交
eb93a3017b
共有 33 個文件被更改,包括 960 次插入291 次删除
  1. 50 5
      dist/preview release/babylon.d.ts
  2. 1 1
      dist/preview release/babylon.js
  3. 336 149
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 102 11
      dist/preview release/babylon.module.d.ts
  6. 50 5
      dist/preview release/documentation.d.ts
  7. 1 1
      dist/preview release/glTF2Interface/package.json
  8. 2 2
      dist/preview release/gui/package.json
  9. 7 7
      dist/preview release/inspector/package.json
  10. 3 3
      dist/preview release/loaders/package.json
  11. 2 2
      dist/preview release/materialsLibrary/package.json
  12. 2 2
      dist/preview release/nodeEditor/package.json
  13. 1 1
      dist/preview release/package.json
  14. 1 1
      dist/preview release/packagesSizeBaseLine.json
  15. 2 2
      dist/preview release/postProcessesLibrary/package.json
  16. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  17. 3 3
      dist/preview release/serializers/package.json
  18. 102 11
      dist/preview release/viewer/babylon.module.d.ts
  19. 12 12
      dist/preview release/viewer/babylon.viewer.js
  20. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  21. 1 0
      dist/preview release/what's new.md
  22. 1 1
      package.json
  23. 178 17
      src/Cameras/XR/features/WebXRControllerPointerSelection.ts
  24. 64 10
      src/Cameras/XR/features/WebXRControllerTeleportation.ts
  25. 15 18
      src/Cameras/XR/webXRFeaturesManager.ts
  26. 2 2
      src/Engines/thinEngine.ts
  27. 12 14
      src/Materials/Textures/baseTexture.ts
  28. 1 1
      src/Meshes/instancedMesh.ts
  29. 1 2
      src/Meshes/linesMesh.ts
  30. 1 1
      src/Meshes/mesh.ts
  31. 1 1
      src/Meshes/transformNode.ts
  32. 1 1
      src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx
  33. 1 1
      src/Shaders/ShadersInclude/pbrVertexDeclaration.fx

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

@@ -19370,7 +19370,7 @@ declare module BABYLON {
          *
          * Returns the clone.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): InstancedMesh;
         /**
          * Disposes the InstancedMesh.
          * Returns nothing.
@@ -19820,7 +19820,7 @@ declare module BABYLON {
         /**
          * Returns a new LineMesh object cloned from the current one.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): LinesMesh;
         /**
          * Creates a new InstancedLinesMesh object from the mesh model.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
@@ -24720,7 +24720,7 @@ declare module BABYLON {
          * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
          * @returns a new mesh
          */
-        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Nullable<AbstractMesh>;
+        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Mesh;
         /**
          * Releases resources associated with this mesh.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -43887,6 +43887,29 @@ declare module BABYLON {
          * Different button type to use instead of the main component
          */
         overrideButtonId?: string;
+        /**
+         * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+         * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+         * 3000 means 3 seconds between pointing at something and selecting it
+         */
+        timeToSelect?: number;
+        /**
+         * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+         * If not disabled, the last picked point will be used to execute a pointer up event
+         * If disabled, pointer up event will be triggered right after the pointer down event.
+         * Used in screen and gaze target ray mode only
+         */
+        disablePointerUpOnTouchOut: boolean;
+        /**
+         * For gaze mode (time to select instead of press)
+         */
+        forceGazeMode: boolean;
+        /**
+         * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+         * to start a new countdown to the pointer down event.
+         * Defaults to 1.
+         */
+        gazeModePointerMovedFactor?: number;
     }
     /**
      * A module that will enable pointer selection for motion controllers of XR Input Sources
@@ -43907,11 +43930,11 @@ declare module BABYLON {
         /**
          * This color will be set to the laser pointer when selection is triggered
          */
-        onPickedLaserPointerColor: Color3;
+        laserPointerPickedColor: Color3;
         /**
          * This color will be applied to the selection ring when selection is triggered
          */
-        onPickedSelectionMeshColor: Color3;
+        selectionMeshPickedColor: Color3;
         /**
          * default color of the selection ring
          */
@@ -43950,7 +43973,19 @@ declare module BABYLON {
          * @returns true if successful.
          */
         detach(): boolean;
+        /**
+         * Get the xr controller that correlates to the pointer id in the pointer event
+         *
+         * @param id the pointer id to search for
+         * @returns the controller that correlates to this id or null if not found
+         */
+        getXRControllerByPointerId(id: number): Nullable<WebXRController>;
         private _attachController;
+        private _attachScreenRayMode;
+        private _attachGazeMode;
+        private _tmpVectorForPickCompare;
+        private _pickingMoved;
+        private _attachTrackedPointerRayMode;
         private _detachController;
         private _generateNewMeshPair;
         private _convertNormalToDirectionOfRay;
@@ -44175,6 +44210,15 @@ declare module BABYLON {
              */
             disableAnimation?: boolean;
         };
+        /**
+         * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+         * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+         */
+        useMainComponentOnly?: boolean;
+        /**
+         * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+         */
+        timeToTeleport?: number;
     }
     /**
      * This is a teleportation feature to be used with webxr-enabled motion controllers.
@@ -44277,6 +44321,7 @@ declare module BABYLON {
         dispose(): void;
         private _currentTeleportationControllerId;
         private _attachController;
+        private _teleportForward;
         private _detachController;
         private createDefaultTargetMesh;
         private setTargetMeshVisibility;

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


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


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


+ 102 - 11
dist/preview release/babylon.module.d.ts

@@ -19909,7 +19909,7 @@ declare module "babylonjs/Meshes/instancedMesh" {
          *
          * Returns the clone.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): InstancedMesh;
         /**
          * Disposes the InstancedMesh.
          * Returns nothing.
@@ -20310,7 +20310,6 @@ declare module "babylonjs/Meshes/linesMesh" {
     import { Material } from "babylonjs/Materials/material";
     import "babylonjs/Shaders/color.fragment";
     import "babylonjs/Shaders/color.vertex";
-    import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
     /**
      * Line mesh
      * @see https://doc.babylonjs.com/babylon101/parametric_shapes
@@ -20392,7 +20391,7 @@ declare module "babylonjs/Meshes/linesMesh" {
         /**
          * Returns a new LineMesh object cloned from the current one.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): LinesMesh;
         /**
          * Creates a new InstancedLinesMesh object from the mesh model.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
@@ -25455,7 +25454,7 @@ declare module "babylonjs/Meshes/mesh" {
          * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
          * @returns a new mesh
          */
-        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Nullable<AbstractMesh>;
+        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Mesh;
         /**
          * Releases resources associated with this mesh.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -45405,6 +45404,8 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
     import { IWebXRFeature } from "babylonjs/Cameras/XR/webXRFeaturesManager";
     import { WebXRSessionManager } from "babylonjs/Cameras/XR/webXRSessionManager";
     import { WebXRInput } from "babylonjs/Cameras/XR/webXRInput";
+    import { WebXRController } from "babylonjs/Cameras/XR/webXRController";
+    import { Nullable } from "babylonjs/types";
     import { Color3 } from "babylonjs/Maths/math.color";
     /**
      * Options interface for the pointer selection module
@@ -45418,6 +45419,29 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
          * Different button type to use instead of the main component
          */
         overrideButtonId?: string;
+        /**
+         * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+         * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+         * 3000 means 3 seconds between pointing at something and selecting it
+         */
+        timeToSelect?: number;
+        /**
+         * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+         * If not disabled, the last picked point will be used to execute a pointer up event
+         * If disabled, pointer up event will be triggered right after the pointer down event.
+         * Used in screen and gaze target ray mode only
+         */
+        disablePointerUpOnTouchOut: boolean;
+        /**
+         * For gaze mode (time to select instead of press)
+         */
+        forceGazeMode: boolean;
+        /**
+         * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+         * to start a new countdown to the pointer down event.
+         * Defaults to 1.
+         */
+        gazeModePointerMovedFactor?: number;
     }
     /**
      * A module that will enable pointer selection for motion controllers of XR Input Sources
@@ -45438,11 +45462,11 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
         /**
          * This color will be set to the laser pointer when selection is triggered
          */
-        onPickedLaserPointerColor: Color3;
+        laserPointerPickedColor: Color3;
         /**
          * This color will be applied to the selection ring when selection is triggered
          */
-        onPickedSelectionMeshColor: Color3;
+        selectionMeshPickedColor: Color3;
         /**
          * default color of the selection ring
          */
@@ -45481,7 +45505,19 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
          * @returns true if successful.
          */
         detach(): boolean;
+        /**
+         * Get the xr controller that correlates to the pointer id in the pointer event
+         *
+         * @param id the pointer id to search for
+         * @returns the controller that correlates to this id or null if not found
+         */
+        getXRControllerByPointerId(id: number): Nullable<WebXRController>;
         private _attachController;
+        private _attachScreenRayMode;
+        private _attachGazeMode;
+        private _tmpVectorForPickCompare;
+        private _pickingMoved;
+        private _attachTrackedPointerRayMode;
         private _detachController;
         private _generateNewMeshPair;
         private _convertNormalToDirectionOfRay;
@@ -45721,6 +45757,15 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerTeleportation" {
              */
             disableAnimation?: boolean;
         };
+        /**
+         * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+         * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+         */
+        useMainComponentOnly?: boolean;
+        /**
+         * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+         */
+        timeToTeleport?: number;
     }
     /**
      * This is a teleportation feature to be used with webxr-enabled motion controllers.
@@ -45823,6 +45868,7 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerTeleportation" {
         dispose(): void;
         private _currentTeleportationControllerId;
         private _attachController;
+        private _teleportForward;
         private _detachController;
         private createDefaultTargetMesh;
         private setTargetMeshVisibility;
@@ -91770,7 +91816,7 @@ declare module BABYLON {
          *
          * Returns the clone.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): InstancedMesh;
         /**
          * Disposes the InstancedMesh.
          * Returns nothing.
@@ -92220,7 +92266,7 @@ declare module BABYLON {
         /**
          * Returns a new LineMesh object cloned from the current one.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): LinesMesh;
         /**
          * Creates a new InstancedLinesMesh object from the mesh model.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
@@ -97120,7 +97166,7 @@ declare module BABYLON {
          * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
          * @returns a new mesh
          */
-        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Nullable<AbstractMesh>;
+        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Mesh;
         /**
          * Releases resources associated with this mesh.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -116287,6 +116333,29 @@ declare module BABYLON {
          * Different button type to use instead of the main component
          */
         overrideButtonId?: string;
+        /**
+         * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+         * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+         * 3000 means 3 seconds between pointing at something and selecting it
+         */
+        timeToSelect?: number;
+        /**
+         * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+         * If not disabled, the last picked point will be used to execute a pointer up event
+         * If disabled, pointer up event will be triggered right after the pointer down event.
+         * Used in screen and gaze target ray mode only
+         */
+        disablePointerUpOnTouchOut: boolean;
+        /**
+         * For gaze mode (time to select instead of press)
+         */
+        forceGazeMode: boolean;
+        /**
+         * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+         * to start a new countdown to the pointer down event.
+         * Defaults to 1.
+         */
+        gazeModePointerMovedFactor?: number;
     }
     /**
      * A module that will enable pointer selection for motion controllers of XR Input Sources
@@ -116307,11 +116376,11 @@ declare module BABYLON {
         /**
          * This color will be set to the laser pointer when selection is triggered
          */
-        onPickedLaserPointerColor: Color3;
+        laserPointerPickedColor: Color3;
         /**
          * This color will be applied to the selection ring when selection is triggered
          */
-        onPickedSelectionMeshColor: Color3;
+        selectionMeshPickedColor: Color3;
         /**
          * default color of the selection ring
          */
@@ -116350,7 +116419,19 @@ declare module BABYLON {
          * @returns true if successful.
          */
         detach(): boolean;
+        /**
+         * Get the xr controller that correlates to the pointer id in the pointer event
+         *
+         * @param id the pointer id to search for
+         * @returns the controller that correlates to this id or null if not found
+         */
+        getXRControllerByPointerId(id: number): Nullable<WebXRController>;
         private _attachController;
+        private _attachScreenRayMode;
+        private _attachGazeMode;
+        private _tmpVectorForPickCompare;
+        private _pickingMoved;
+        private _attachTrackedPointerRayMode;
         private _detachController;
         private _generateNewMeshPair;
         private _convertNormalToDirectionOfRay;
@@ -116575,6 +116656,15 @@ declare module BABYLON {
              */
             disableAnimation?: boolean;
         };
+        /**
+         * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+         * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+         */
+        useMainComponentOnly?: boolean;
+        /**
+         * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+         */
+        timeToTeleport?: number;
     }
     /**
      * This is a teleportation feature to be used with webxr-enabled motion controllers.
@@ -116677,6 +116767,7 @@ declare module BABYLON {
         dispose(): void;
         private _currentTeleportationControllerId;
         private _attachController;
+        private _teleportForward;
         private _detachController;
         private createDefaultTargetMesh;
         private setTargetMeshVisibility;

+ 50 - 5
dist/preview release/documentation.d.ts

@@ -19370,7 +19370,7 @@ declare module BABYLON {
          *
          * Returns the clone.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): InstancedMesh;
         /**
          * Disposes the InstancedMesh.
          * Returns nothing.
@@ -19820,7 +19820,7 @@ declare module BABYLON {
         /**
          * Returns a new LineMesh object cloned from the current one.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): LinesMesh;
         /**
          * Creates a new InstancedLinesMesh object from the mesh model.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
@@ -24720,7 +24720,7 @@ declare module BABYLON {
          * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
          * @returns a new mesh
          */
-        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Nullable<AbstractMesh>;
+        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Mesh;
         /**
          * Releases resources associated with this mesh.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -43887,6 +43887,29 @@ declare module BABYLON {
          * Different button type to use instead of the main component
          */
         overrideButtonId?: string;
+        /**
+         * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+         * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+         * 3000 means 3 seconds between pointing at something and selecting it
+         */
+        timeToSelect?: number;
+        /**
+         * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+         * If not disabled, the last picked point will be used to execute a pointer up event
+         * If disabled, pointer up event will be triggered right after the pointer down event.
+         * Used in screen and gaze target ray mode only
+         */
+        disablePointerUpOnTouchOut: boolean;
+        /**
+         * For gaze mode (time to select instead of press)
+         */
+        forceGazeMode: boolean;
+        /**
+         * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+         * to start a new countdown to the pointer down event.
+         * Defaults to 1.
+         */
+        gazeModePointerMovedFactor?: number;
     }
     /**
      * A module that will enable pointer selection for motion controllers of XR Input Sources
@@ -43907,11 +43930,11 @@ declare module BABYLON {
         /**
          * This color will be set to the laser pointer when selection is triggered
          */
-        onPickedLaserPointerColor: Color3;
+        laserPointerPickedColor: Color3;
         /**
          * This color will be applied to the selection ring when selection is triggered
          */
-        onPickedSelectionMeshColor: Color3;
+        selectionMeshPickedColor: Color3;
         /**
          * default color of the selection ring
          */
@@ -43950,7 +43973,19 @@ declare module BABYLON {
          * @returns true if successful.
          */
         detach(): boolean;
+        /**
+         * Get the xr controller that correlates to the pointer id in the pointer event
+         *
+         * @param id the pointer id to search for
+         * @returns the controller that correlates to this id or null if not found
+         */
+        getXRControllerByPointerId(id: number): Nullable<WebXRController>;
         private _attachController;
+        private _attachScreenRayMode;
+        private _attachGazeMode;
+        private _tmpVectorForPickCompare;
+        private _pickingMoved;
+        private _attachTrackedPointerRayMode;
         private _detachController;
         private _generateNewMeshPair;
         private _convertNormalToDirectionOfRay;
@@ -44175,6 +44210,15 @@ declare module BABYLON {
              */
             disableAnimation?: boolean;
         };
+        /**
+         * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+         * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+         */
+        useMainComponentOnly?: boolean;
+        /**
+         * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+         */
+        timeToTeleport?: number;
     }
     /**
      * This is a teleportation feature to be used with webxr-enabled motion controllers.
@@ -44277,6 +44321,7 @@ declare module BABYLON {
         dispose(): void;
         private _currentTeleportationControllerId;
         private _attachController;
+        private _teleportForward;
         private _detachController;
         private createDefaultTargetMesh;
         private setTargetMeshVisibility;

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

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

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.1.0-beta.18",
+    "version": "4.1.0-beta.20",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.18"
+        "babylonjs": "4.1.0-beta.20"
     },
     "engines": {
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.1.0-beta.18",
+    "version": "4.1.0-beta.20",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.18",
-        "babylonjs-gui": "4.1.0-beta.18",
-        "babylonjs-loaders": "4.1.0-beta.18",
-        "babylonjs-materials": "4.1.0-beta.18",
-        "babylonjs-serializers": "4.1.0-beta.18",
-        "babylonjs-gltf2interface": "4.1.0-beta.18"
+        "babylonjs": "4.1.0-beta.20",
+        "babylonjs-gui": "4.1.0-beta.20",
+        "babylonjs-loaders": "4.1.0-beta.20",
+        "babylonjs-materials": "4.1.0-beta.20",
+        "babylonjs-serializers": "4.1.0-beta.20",
+        "babylonjs-gltf2interface": "4.1.0-beta.20"
     },
     "devDependencies": {
         "@types/react": "~16.7.3",

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.1.0-beta.18",
+    "version": "4.1.0-beta.20",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.1.0-beta.18",
-        "babylonjs": "4.1.0-beta.18"
+        "babylonjs-gltf2interface": "4.1.0-beta.20",
+        "babylonjs": "4.1.0-beta.20"
     },
     "engines": {
         "node": "*"

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

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

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

@@ -4,14 +4,14 @@
     },
     "name": "babylonjs-node-editor",
     "description": "The Babylon.js node material editor.",
-    "version": "4.1.0-beta.18",
+    "version": "4.1.0-beta.20",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.18"
+        "babylonjs": "4.1.0-beta.20"
     },
     "files": [
         "babylon.nodeEditor.max.js.map",

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

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.1.0-beta.18",
+    "version": "4.1.0-beta.20",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/packagesSizeBaseLine.json

@@ -1 +1 @@
-{"thinEngineOnly":112302,"engineOnly":148393,"sceneOnly":502176,"minGridMaterial":632872,"minStandardMaterial":772863}
+{"thinEngineOnly":112302,"engineOnly":148393,"sceneOnly":502176,"minGridMaterial":632872,"minStandardMaterial":772862}

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

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

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

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

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

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

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

@@ -19909,7 +19909,7 @@ declare module "babylonjs/Meshes/instancedMesh" {
          *
          * Returns the clone.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): InstancedMesh;
         /**
          * Disposes the InstancedMesh.
          * Returns nothing.
@@ -20310,7 +20310,6 @@ declare module "babylonjs/Meshes/linesMesh" {
     import { Material } from "babylonjs/Materials/material";
     import "babylonjs/Shaders/color.fragment";
     import "babylonjs/Shaders/color.vertex";
-    import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
     /**
      * Line mesh
      * @see https://doc.babylonjs.com/babylon101/parametric_shapes
@@ -20392,7 +20391,7 @@ declare module "babylonjs/Meshes/linesMesh" {
         /**
          * Returns a new LineMesh object cloned from the current one.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): LinesMesh;
         /**
          * Creates a new InstancedLinesMesh object from the mesh model.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
@@ -25455,7 +25454,7 @@ declare module "babylonjs/Meshes/mesh" {
          * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
          * @returns a new mesh
          */
-        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Nullable<AbstractMesh>;
+        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Mesh;
         /**
          * Releases resources associated with this mesh.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -45405,6 +45404,8 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
     import { IWebXRFeature } from "babylonjs/Cameras/XR/webXRFeaturesManager";
     import { WebXRSessionManager } from "babylonjs/Cameras/XR/webXRSessionManager";
     import { WebXRInput } from "babylonjs/Cameras/XR/webXRInput";
+    import { WebXRController } from "babylonjs/Cameras/XR/webXRController";
+    import { Nullable } from "babylonjs/types";
     import { Color3 } from "babylonjs/Maths/math.color";
     /**
      * Options interface for the pointer selection module
@@ -45418,6 +45419,29 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
          * Different button type to use instead of the main component
          */
         overrideButtonId?: string;
+        /**
+         * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+         * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+         * 3000 means 3 seconds between pointing at something and selecting it
+         */
+        timeToSelect?: number;
+        /**
+         * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+         * If not disabled, the last picked point will be used to execute a pointer up event
+         * If disabled, pointer up event will be triggered right after the pointer down event.
+         * Used in screen and gaze target ray mode only
+         */
+        disablePointerUpOnTouchOut: boolean;
+        /**
+         * For gaze mode (time to select instead of press)
+         */
+        forceGazeMode: boolean;
+        /**
+         * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+         * to start a new countdown to the pointer down event.
+         * Defaults to 1.
+         */
+        gazeModePointerMovedFactor?: number;
     }
     /**
      * A module that will enable pointer selection for motion controllers of XR Input Sources
@@ -45438,11 +45462,11 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
         /**
          * This color will be set to the laser pointer when selection is triggered
          */
-        onPickedLaserPointerColor: Color3;
+        laserPointerPickedColor: Color3;
         /**
          * This color will be applied to the selection ring when selection is triggered
          */
-        onPickedSelectionMeshColor: Color3;
+        selectionMeshPickedColor: Color3;
         /**
          * default color of the selection ring
          */
@@ -45481,7 +45505,19 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerPointerSelection" {
          * @returns true if successful.
          */
         detach(): boolean;
+        /**
+         * Get the xr controller that correlates to the pointer id in the pointer event
+         *
+         * @param id the pointer id to search for
+         * @returns the controller that correlates to this id or null if not found
+         */
+        getXRControllerByPointerId(id: number): Nullable<WebXRController>;
         private _attachController;
+        private _attachScreenRayMode;
+        private _attachGazeMode;
+        private _tmpVectorForPickCompare;
+        private _pickingMoved;
+        private _attachTrackedPointerRayMode;
         private _detachController;
         private _generateNewMeshPair;
         private _convertNormalToDirectionOfRay;
@@ -45721,6 +45757,15 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerTeleportation" {
              */
             disableAnimation?: boolean;
         };
+        /**
+         * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+         * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+         */
+        useMainComponentOnly?: boolean;
+        /**
+         * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+         */
+        timeToTeleport?: number;
     }
     /**
      * This is a teleportation feature to be used with webxr-enabled motion controllers.
@@ -45823,6 +45868,7 @@ declare module "babylonjs/Cameras/XR/features/WebXRControllerTeleportation" {
         dispose(): void;
         private _currentTeleportationControllerId;
         private _attachController;
+        private _teleportForward;
         private _detachController;
         private createDefaultTargetMesh;
         private setTargetMeshVisibility;
@@ -91770,7 +91816,7 @@ declare module BABYLON {
          *
          * Returns the clone.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): InstancedMesh;
         /**
          * Disposes the InstancedMesh.
          * Returns nothing.
@@ -92220,7 +92266,7 @@ declare module BABYLON {
         /**
          * Returns a new LineMesh object cloned from the current one.
          */
-        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<AbstractMesh>;
+        clone(name: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean): LinesMesh;
         /**
          * Creates a new InstancedLinesMesh object from the mesh model.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
@@ -97120,7 +97166,7 @@ declare module BABYLON {
          * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
          * @returns a new mesh
          */
-        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Nullable<AbstractMesh>;
+        clone(name?: string, newParent?: Nullable<Node>, doNotCloneChildren?: boolean, clonePhysicsImpostor?: boolean): Mesh;
         /**
          * Releases resources associated with this mesh.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -116287,6 +116333,29 @@ declare module BABYLON {
          * Different button type to use instead of the main component
          */
         overrideButtonId?: string;
+        /**
+         * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+         * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+         * 3000 means 3 seconds between pointing at something and selecting it
+         */
+        timeToSelect?: number;
+        /**
+         * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+         * If not disabled, the last picked point will be used to execute a pointer up event
+         * If disabled, pointer up event will be triggered right after the pointer down event.
+         * Used in screen and gaze target ray mode only
+         */
+        disablePointerUpOnTouchOut: boolean;
+        /**
+         * For gaze mode (time to select instead of press)
+         */
+        forceGazeMode: boolean;
+        /**
+         * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+         * to start a new countdown to the pointer down event.
+         * Defaults to 1.
+         */
+        gazeModePointerMovedFactor?: number;
     }
     /**
      * A module that will enable pointer selection for motion controllers of XR Input Sources
@@ -116307,11 +116376,11 @@ declare module BABYLON {
         /**
          * This color will be set to the laser pointer when selection is triggered
          */
-        onPickedLaserPointerColor: Color3;
+        laserPointerPickedColor: Color3;
         /**
          * This color will be applied to the selection ring when selection is triggered
          */
-        onPickedSelectionMeshColor: Color3;
+        selectionMeshPickedColor: Color3;
         /**
          * default color of the selection ring
          */
@@ -116350,7 +116419,19 @@ declare module BABYLON {
          * @returns true if successful.
          */
         detach(): boolean;
+        /**
+         * Get the xr controller that correlates to the pointer id in the pointer event
+         *
+         * @param id the pointer id to search for
+         * @returns the controller that correlates to this id or null if not found
+         */
+        getXRControllerByPointerId(id: number): Nullable<WebXRController>;
         private _attachController;
+        private _attachScreenRayMode;
+        private _attachGazeMode;
+        private _tmpVectorForPickCompare;
+        private _pickingMoved;
+        private _attachTrackedPointerRayMode;
         private _detachController;
         private _generateNewMeshPair;
         private _convertNormalToDirectionOfRay;
@@ -116575,6 +116656,15 @@ declare module BABYLON {
              */
             disableAnimation?: boolean;
         };
+        /**
+         * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+         * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+         */
+        useMainComponentOnly?: boolean;
+        /**
+         * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+         */
+        timeToTeleport?: number;
     }
     /**
      * This is a teleportation feature to be used with webxr-enabled motion controllers.
@@ -116677,6 +116767,7 @@ declare module BABYLON {
         dispose(): void;
         private _currentTeleportationControllerId;
         private _attachController;
+        private _teleportForward;
         private _detachController;
         private createDefaultTargetMesh;
         private setTargetMeshVisibility;

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


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


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

@@ -190,6 +190,7 @@
 - Teleportation allows selecting direction before teleporting when using thumbstick/touchpad. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
 - It is now possible to force a certain profile type for the controllers ([#7348](https://github.com/BabylonJS/Babylon.js/issues/7375)) ([RaananW](https://github.com/RaananW/))
 - WebXR camera is initialized on the first frame ([#7389](https://github.com/BabylonJS/Babylon.js/issues/7389)) ([RaananW](https://github.com/RaananW/))
+- Selection has forcable gaze mode and touch-screen support ([#7395](https://github.com/BabylonJS/Babylon.js/issues/7395)) ([RaananW](https://github.com/RaananW/))
 
 ### Ray
 

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.1.0-beta.18",
+    "version": "4.1.0-beta.20",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 178 - 17
src/Cameras/XR/features/WebXRControllerPointerSelection.ts

@@ -30,6 +30,32 @@ export interface IWebXRControllerPointerSelectionOptions {
      * Different button type to use instead of the main component
      */
     overrideButtonId?: string;
+    /**
+     * The amount of time in miliseconds it takes between pick found something to a pointer down event.
+     * Used in gaze modes. Tracked pointer uses the trigger, screen uses touch events
+     * 3000 means 3 seconds between pointing at something and selecting it
+     */
+    timeToSelect?: number;
+
+    /**
+     * Disable the pointer up event when the xr controller in screen and gaze mode is disposed (meaning - when the user removed the finger from the screen)
+     * If not disabled, the last picked point will be used to execute a pointer up event
+     * If disabled, pointer up event will be triggered right after the pointer down event.
+     * Used in screen and gaze target ray mode only
+     */
+    disablePointerUpOnTouchOut: boolean;
+
+    /**
+     * For gaze mode (time to select instead of press)
+     */
+    forceGazeMode: boolean;
+
+    /**
+     * Factor to be applied to the pointer-moved function in the gaze mode. How sensitive should the gaze mode be when checking if the pointer moved
+     * to start a new countdown to the pointer down event.
+     * Defaults to 1.
+     */
+    gazeModePointerMovedFactor?: number;
 }
 
 /**
@@ -51,11 +77,11 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
     /**
      * This color will be set to the laser pointer when selection is triggered
      */
-    public onPickedLaserPointerColor: Color3 = new Color3(0.7, 0.7, 0.7);
+    public laserPointerPickedColor: Color3 = new Color3(0.7, 0.7, 0.7);
     /**
      * This color will be applied to the selection ring when selection is triggered
      */
-    public onPickedSelectionMeshColor: Color3 = new Color3(0.7, 0.7, 0.7);
+    public selectionMeshPickedColor: Color3 = new Color3(0.7, 0.7, 0.7);
     /**
      * default color of the selection ring
      */
@@ -76,6 +102,7 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
             xrController: WebXRController;
             selectionComponent?: WebXRControllerComponent;
             onButtonChangedObserver?: Nullable<Observer<WebXRControllerComponent>>;
+            onFrameObserver?: Nullable<Observer<XRFrame>>;
             laserPointer: AbstractMesh;
             selectionMesh: AbstractMesh;
             pick: Nullable<PickingInfo>;
@@ -124,14 +151,6 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
                 controllerData.xrController.getWorldPointerRayToRef(this._tmpRay);
                 controllerData.pick = this._scene.pickWithRay(this._tmpRay);
 
-                if (controllerData.selectionComponent && controllerData.selectionComponent.pressed) {
-                    (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.onPickedSelectionMeshColor;
-                    (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.onPickedLaserPointerColor;
-                } else {
-                    (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshDefaultColor;
-                    (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.lasterPointerDefaultColor;
-                }
-
                 const pick = controllerData.pick;
 
                 if (pick && pick.pickedPoint && pick.hit) {
@@ -187,17 +206,29 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
         return true;
     }
 
-    private _attachController = (xrController: WebXRController) => {
-        // only support tracker pointer
-        if (xrController.inputSource.targetRayMode !== "tracked-pointer") {
-            return;
+    /**
+     * Get the xr controller that correlates to the pointer id in the pointer event
+     *
+     * @param id the pointer id to search for
+     * @returns the controller that correlates to this id or null if not found
+     */
+    public getXRControllerByPointerId(id: number): Nullable<WebXRController> {
+        const keys = Object.keys(this._controllers);
+
+        for (let i = 0; i < keys.length; ++i) {
+            if (this._controllers[keys[i]].id === id) {
+                return this._controllers[keys[i]].xrController;
+            }
         }
+        return null;
+    }
 
-        if (this._controllers[xrController.uniqueId] || !xrController.gamepadController) {
+    private _attachController = (xrController: WebXRController) => {
+        if (this._controllers[xrController.uniqueId]) {
             // already attached
             return;
         }
-
+        // only support tracker pointer
         const { laserPointer, selectionMesh } = this._generateNewMeshPair(xrController);
 
         // get two new meshes
@@ -208,6 +239,124 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
             pick: null,
             id: WebXRControllerPointerSelection._idCounter++
         };
+        switch (xrController.inputSource.targetRayMode) {
+            case "tracked-pointer":
+                return this._attachTrackedPointerRayMode(xrController);
+            case "gaze":
+                return this._attachGazeMode(xrController);
+            case "screen":
+                return this._attachScreenRayMode(xrController);
+        }
+    }
+
+    private _attachScreenRayMode(xrController: WebXRController) {
+        const controllerData = this._controllers[xrController.uniqueId];
+        let downTriggered = false;
+        controllerData.onFrameObserver = this._xrSessionManager.onXRFrameObservable.add(() => {
+            if (!controllerData.pick || (this._options.disablePointerUpOnTouchOut && downTriggered)) { return; }
+            if (!downTriggered) {
+                this._scene.simulatePointerDown(controllerData.pick, { pointerId: controllerData.id });
+                downTriggered = true;
+                if (this._options.disablePointerUpOnTouchOut) {
+                    this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+                }
+            } else {
+                this._scene.simulatePointerMove(controllerData.pick, { pointerId: controllerData.id });
+            }
+        });
+        xrController.onDisposeObservable.addOnce(() => {
+            if (controllerData.pick && downTriggered && !this._options.disablePointerUpOnTouchOut) {
+                this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+            }
+        });
+    }
+
+    private _attachGazeMode(xrController: WebXRController) {
+        const controllerData = this._controllers[xrController.uniqueId];
+        // attached when touched, detaches when raised
+        const timeToSelect = this._options.timeToSelect || 3000;
+        let oldPick = new PickingInfo();
+        let discMesh = TorusBuilder.CreateTorus("selection", {
+            diameter: 0.0035 * 15,
+            thickness: 0.0025 * 6,
+            tessellation: 20
+        }, this._scene);
+        discMesh.isVisible = false;
+        discMesh.isPickable = false;
+        discMesh.parent = controllerData.selectionMesh;
+        let timer = 0;
+        let downTriggered = false;
+        controllerData.onFrameObserver = this._xrSessionManager.onXRFrameObservable.add(() => {
+            if (!controllerData.pick) { return; }
+            discMesh.isVisible = false;
+            if (controllerData.pick.hit) {
+                if (!this._pickingMoved(oldPick, controllerData.pick)) {
+                    if (timer > timeToSelect / 10) {
+                        discMesh.isVisible = true;
+                    }
+
+                    timer += this._scene.getEngine().getDeltaTime();
+                    if (timer >= timeToSelect) {
+                        this._scene.simulatePointerDown(controllerData.pick, { pointerId: controllerData.id });
+                        downTriggered = true;
+                        // pointer up right after down, if disable on touch out
+                        if (this._options.disablePointerUpOnTouchOut) {
+                            this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+                        } else {
+                            this._scene.simulatePointerMove(controllerData.pick, { pointerId: controllerData.id });
+                        }
+                        discMesh.isVisible = false;
+                    } else {
+                        const scaleFactor = 1 - (timer / timeToSelect);
+                        discMesh.scaling.set(scaleFactor, scaleFactor, scaleFactor);
+                    }
+                } else {
+                    if (downTriggered) {
+                        if (!this._options.disablePointerUpOnTouchOut) {
+                            this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+                        }
+                    }
+                    downTriggered = false;
+                    timer = 0;
+                }
+            } else {
+                downTriggered = false;
+                timer = 0;
+            }
+
+            oldPick = controllerData.pick;
+        });
+        xrController.onDisposeObservable.addOnce(() => {
+            if (controllerData.pick && !this._options.disablePointerUpOnTouchOut && downTriggered) {
+                this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+            }
+            discMesh.dispose();
+        });
+    }
+    private _tmpVectorForPickCompare = new Vector3();
+
+    private _pickingMoved(oldPick: PickingInfo, newPick: PickingInfo) {
+        if (!oldPick.hit || !newPick.hit) { return true; }
+        if (!oldPick.pickedMesh || !oldPick.pickedPoint || !newPick.pickedMesh || !newPick.pickedPoint) { return true; }
+        if (oldPick.pickedMesh !== newPick.pickedMesh) { return true; }
+        oldPick.pickedPoint?.subtractToRef(newPick.pickedPoint, this._tmpVectorForPickCompare);
+        this._tmpVectorForPickCompare.set(Math.abs(this._tmpVectorForPickCompare.x), Math.abs(this._tmpVectorForPickCompare.y), Math.abs(this._tmpVectorForPickCompare.z));
+        const delta = (this._options.gazeModePointerMovedFactor || 1) * 0.01 / newPick.distance;
+        const length = this._tmpVectorForPickCompare.length();
+        if (length > delta) { return true; }
+        return false;
+
+    }
+
+    private _attachTrackedPointerRayMode(xrController: WebXRController) {
+        if (!xrController.gamepadController) {
+            return;
+        }
+
+        if (this._options.forceGazeMode) {
+            return this._attachGazeMode(xrController);
+        }
+
         const controllerData = this._controllers[xrController.uniqueId];
 
         if (this._options.overrideButtonId) {
@@ -219,6 +368,16 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
 
         let observer: Nullable<Observer<XRFrame>> = null;
 
+        controllerData.onFrameObserver = this._xrSessionManager.onXRFrameObservable.add(() => {
+            if (controllerData.selectionComponent && controllerData.selectionComponent.pressed) {
+                (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshPickedColor;
+                (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.laserPointerPickedColor;
+            } else {
+                (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshDefaultColor;
+                (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.lasterPointerDefaultColor;
+            }
+        });
+
         controllerData.onButtonChangedObserver = controllerData.selectionComponent.onButtonStateChanged.add((component) => {
             if (component.changes.pressed) {
                 const pressed = component.changes.pressed.current;
@@ -237,7 +396,6 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
                 }
             }
         });
-
     }
 
     private _detachController(xrControllerUniqueId: string) {
@@ -248,6 +406,9 @@ export class WebXRControllerPointerSelection implements IWebXRFeature {
                 controllerData.selectionComponent.onButtonStateChanged.remove(controllerData.onButtonChangedObserver);
             }
         }
+        if (controllerData.onFrameObserver) {
+            this._xrSessionManager.onXRFrameObservable.remove(controllerData.onFrameObserver);
+        }
         controllerData.selectionMesh.dispose();
         controllerData.laserPointer.dispose();
         // remove from the map

+ 64 - 10
src/Cameras/XR/features/WebXRControllerTeleportation.ts

@@ -65,6 +65,17 @@ export interface IWebXRTeleportationOptions {
          */
         disableAnimation?: boolean;
     };
+
+    /**
+     * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
+     * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
+     */
+    useMainComponentOnly?: boolean;
+
+    /**
+     * If main component is used (no thumbstick), how long should the "long press" take before teleporting
+     */
+    timeToTeleport?: number;
 }
 
 /**
@@ -325,20 +336,48 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         // motion controller support
         if (xrController.gamepadController) {
             const movementController = xrController.gamepadController.getComponent(WebXRControllerComponent.THUMBSTICK) || xrController.gamepadController.getComponent(WebXRControllerComponent.TOUCHPAD);
-            if (!movementController) {
+            if (!movementController || this._options.useMainComponentOnly) {
                 // use trigger to move on long press
+                const mainComponent = xrController.gamepadController.getMainComponent();
+                if (!mainComponent) {
+                    return;
+                }
+                controllerData.onButtonChangedObserver = mainComponent.onButtonStateChanged.add(() => {
+                    // did "pressed" changed?
+                    if (mainComponent.changes.pressed) {
+                        if (mainComponent.changes.pressed.current) {
+                            // simulate "forward" thumbstick push
+                            controllerData.teleportationState.forward = true;
+                            this._currentTeleportationControllerId = controllerData.xrController.uniqueId;
+                            controllerData.teleportationState.baseRotation = this._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y;
+                            controllerData.teleportationState.currentRotation = 0;
+                            const timeToSelect = this._options.timeToTeleport || 3000;
+                            let timer = 0;
+                            const observer = this._xrSessionManager.onXRFrameObservable.add(() => {
+                                if (!mainComponent.pressed) {
+                                    this._xrSessionManager.onXRFrameObservable.remove(observer);
+                                    return;
+                                }
+                                timer += this._xrSessionManager.scene.getEngine().getDeltaTime();
+                                if (timer >= timeToSelect && this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward) {
+                                    this._teleportForward(xrController.uniqueId);
+                                }
+
+                                // failsafe
+                                if (timer >= timeToSelect) {
+                                    this._xrSessionManager.onXRFrameObservable.remove(observer);
+                                }
+                            });
+                        } else {
+                            controllerData.teleportationState.forward = false;
+                            this._currentTeleportationControllerId = "";
+                        }
+                    }
+                });
             } else {
                 controllerData.onButtonChangedObserver = movementController.onButtonStateChanged.add(() => {
                     if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward && !movementController.touched) {
-                        controllerData.teleportationState.forward = false;
-                        this._currentTeleportationControllerId = "";
-                        // do the movement forward here
-                        if (this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.isVisible) {
-                            const height = this._options.xrInput.xrCamera.position.y - this._options.teleportationTargetMesh.position.y;
-                            this._options.xrInput.xrCamera.position.copyFrom(this._options.teleportationTargetMesh.position);
-                            this._options.xrInput.xrCamera.position.y += height;
-                            this._options.xrInput.xrCamera.rotationQuaternion.multiplyInPlace(Quaternion.FromEulerAngles(0, controllerData.teleportationState.currentRotation, 0));
-                        }
+                        this._teleportForward(xrController.uniqueId);
                     }
                 });
                 // use thumbstick (or touchpad if thumbstick not available)
@@ -390,6 +429,8 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
                                     setTimeout(() => {
                                         controllerData.teleportationState.currentRotation = Math.atan2(axesData.x, -axesData.y);
                                     });
+                                } else {
+                                    controllerData.teleportationState.currentRotation = 0;
                                 }
                             }
                         }
@@ -401,6 +442,19 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         }
     }
 
+    private _teleportForward(controllerId: string) {
+        const controllerData = this._controllers[controllerId];
+        controllerData.teleportationState.forward = false;
+        this._currentTeleportationControllerId = "";
+        // do the movement forward here
+        if (this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.isVisible) {
+            const height = this._options.xrInput.xrCamera.position.y - this._options.teleportationTargetMesh.position.y;
+            this._options.xrInput.xrCamera.position.copyFrom(this._options.teleportationTargetMesh.position);
+            this._options.xrInput.xrCamera.position.y += height;
+            this._options.xrInput.xrCamera.rotationQuaternion.multiplyInPlace(Quaternion.FromEulerAngles(0, controllerData.teleportationState.currentRotation, 0));
+        }
+    }
+
     private _detachController(xrControllerUniqueId: string) {
         const controllerData = this._controllers[xrControllerUniqueId];
         if (!controllerData) { return; }

+ 15 - 18
src/Cameras/XR/webXRFeaturesManager.ts

@@ -183,27 +183,24 @@ export class WebXRFeaturesManager implements IDisposable {
         }
         // check if already initialized
         const feature = this._features[name];
-        if (!feature || !feature.featureImplementation || feature.version !== versionToLoad) {
-            const constructFunction = WebXRFeaturesManager.ConstructFeature(name, versionToLoad, this._xrSessionManager, moduleOptions);
-            if (!constructFunction) {
-                // report error?
-                throw new Error(`feature not found - ${name}`);
-            }
-
-            if (feature) {
-                this.disableFeature(name);
-            }
+        const constructFunction = WebXRFeaturesManager.ConstructFeature(name, versionToLoad, this._xrSessionManager, moduleOptions);
+        if (!constructFunction) {
+            // report error?
+            throw new Error(`feature not found - ${name}`);
+        }
 
-            this._features[name] = {
-                featureImplementation: constructFunction(),
-                enabled: true,
-                version: versionToLoad
-            };
-        } else {
-            // make sure it is enabled now:
-            feature.enabled = true;
+        /* If the feature is already enabled, detach and dispose it, and create a new one */
+        if (feature) {
+            this.disableFeature(name);
+            feature.featureImplementation.dispose();
         }
 
+        this._features[name] = {
+            featureImplementation: constructFunction(),
+            enabled: true,
+            version: versionToLoad
+        };
+
         // if session started already, request and enable
         if (this._xrSessionManager.session && !feature.featureImplementation.attached && attachIfPossible) {
             // enable feature

+ 2 - 2
src/Engines/thinEngine.ts

@@ -132,14 +132,14 @@ export class ThinEngine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.1.0-beta.19";
+        return "babylonjs@4.1.0-beta.20";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.1.0-beta.19";
+        return "4.1.0-beta.20";
     }
 
     /**

+ 12 - 14
src/Materials/Textures/baseTexture.ts

@@ -706,23 +706,21 @@ export class BaseTexture implements IAnimatable {
      * Dispose the texture and release its associated resources.
      */
     public dispose(): void {
-        if (!this._scene) {
-            return;
-        }
-
-        // Animations
-        if (this._scene.stopAnimation) {
-            this._scene.stopAnimation(this);
-        }
+        if (this._scene) {
+            // Animations
+            if (this._scene.stopAnimation) {
+                this._scene.stopAnimation(this);
+            }
 
-        // Remove from scene
-        this._scene._removePendingData(this);
-        var index = this._scene.textures.indexOf(this);
+            // Remove from scene
+            this._scene._removePendingData(this);
+            var index = this._scene.textures.indexOf(this);
 
-        if (index >= 0) {
-            this._scene.textures.splice(index, 1);
+            if (index >= 0) {
+                this._scene.textures.splice(index, 1);
+            }
+            this._scene.onTextureRemovedObservable.notifyObservers(this);
         }
-        this._scene.onTextureRemovedObservable.notifyObservers(this);
 
         if (this._texture === undefined) {
             return;

+ 1 - 1
src/Meshes/instancedMesh.ts

@@ -390,7 +390,7 @@ export class InstancedMesh extends AbstractMesh {
      *
      * Returns the clone.
      */
-    public clone(name: string, newParent: Nullable<Node>= null, doNotCloneChildren?: boolean): Nullable<AbstractMesh> {
+    public clone(name: string, newParent: Nullable<Node>= null, doNotCloneChildren?: boolean): InstancedMesh {
         var result = this._sourceMesh.createInstance(name);
 
         // Deep copy

+ 1 - 2
src/Meshes/linesMesh.ts

@@ -13,7 +13,6 @@ import { MaterialHelper } from '../Materials/materialHelper';
 
 import "../Shaders/color.fragment";
 import "../Shaders/color.vertex";
-import { AbstractMesh } from './abstractMesh';
 
 /**
  * Line mesh
@@ -224,7 +223,7 @@ export class LinesMesh extends Mesh {
     /**
      * Returns a new LineMesh object cloned from the current one.
      */
-    public clone(name: string, newParent: Nullable<Node> = null, doNotCloneChildren?: boolean): Nullable<AbstractMesh> {
+    public clone(name: string, newParent: Nullable<Node> = null, doNotCloneChildren?: boolean): LinesMesh {
         return new LinesMesh(name, this.getScene(), newParent, this, doNotCloneChildren);
     }
 

+ 1 - 1
src/Meshes/mesh.ts

@@ -2201,7 +2201,7 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
      * @param clonePhysicsImpostor allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any (default `true`)
      * @returns a new mesh
      */
-    public clone(name: string = "", newParent: Nullable<Node> = null, doNotCloneChildren?: boolean, clonePhysicsImpostor: boolean = true): Nullable<AbstractMesh> {
+    public clone(name: string = "", newParent: Nullable<Node> = null, doNotCloneChildren?: boolean, clonePhysicsImpostor: boolean = true): Mesh {
         return new Mesh(name, this.getScene(), newParent, this, doNotCloneChildren, clonePhysicsImpostor);
     }
 

+ 1 - 1
src/Meshes/transformNode.ts

@@ -1224,7 +1224,7 @@ export class TransformNode extends Node {
      * @param doNotCloneChildren Do not clone children hierarchy
      * @returns the new transform node
      */
-    public clone(name: string, newParent: Nullable<Node>, doNotCloneChildren?: boolean): Nullable<TransformNode> {
+    public clone(name: string, newParent: Nullable<Node>, doNotCloneChildren?: boolean) : Nullable<TransformNode> {
         var result = SerializationHelper.Clone(() => new TransformNode(name, this.getScene()), this);
 
         result.name = name;

+ 1 - 1
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -117,7 +117,7 @@ uniform mat4 view;
 
     #ifdef SS_THICKNESSANDMASK_TEXTURE
         uniform vec2 vThicknessInfos;
-        uniform mat4 thicknessMatrix;;
+        uniform mat4 thicknessMatrix;
     #endif
 
     uniform vec2 vThicknessParam;

+ 1 - 1
src/Shaders/ShadersInclude/pbrVertexDeclaration.fx

@@ -94,7 +94,7 @@ uniform float pointSize;
 
     #ifdef SS_THICKNESSANDMASK_TEXTURE
         uniform vec2 vThicknessInfos;
-        uniform mat4 thicknessMatrix;;
+        uniform mat4 thicknessMatrix;
     #endif
 #endif