Explorar o código

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

Popov72 %!s(int64=5) %!d(string=hai) anos
pai
achega
674cff8ce1
Modificáronse 86 ficheiros con 2762 adicións e 761 borrados
  1. 57 52
      Playground/src/components/examplesComponent.tsx
  2. 4 0
      Playground/src/scss/examples.scss
  3. 24 18
      Playground/src/tools/loadManager.ts
  4. 22 0
      dist/ktx2Transcoders/msc_basis_transcoder.js
  5. BIN=BIN
      dist/ktx2Transcoders/msc_basis_transcoder.wasm
  6. BIN=BIN
      dist/ktx2Transcoders/uastc_astc.wasm
  7. BIN=BIN
      dist/ktx2Transcoders/uastc_bc7.wasm
  8. 159 43
      dist/preview release/babylon.d.ts
  9. 2 2
      dist/preview release/babylon.js
  10. 326 64
      dist/preview release/babylon.max.js
  11. 1 1
      dist/preview release/babylon.max.js.map
  12. 312 86
      dist/preview release/babylon.module.d.ts
  13. 176 48
      dist/preview release/documentation.d.ts
  14. 12 5
      dist/preview release/glTF2Interface/babylon.glTF2Interface.d.ts
  15. 1 1
      dist/preview release/glTF2Interface/package.json
  16. 48 48
      dist/preview release/gui/babylon.gui.js
  17. 1 1
      dist/preview release/gui/babylon.gui.js.map
  18. 2 2
      dist/preview release/gui/package.json
  19. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  20. 63 8
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  21. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  22. 12 0
      dist/preview release/inspector/babylon.inspector.d.ts
  23. 30 0
      dist/preview release/inspector/babylon.inspector.module.d.ts
  24. 7 7
      dist/preview release/inspector/package.json
  25. 2 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  26. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.js.map
  27. 16 5
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  28. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  29. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  30. 16 5
      dist/preview release/loaders/babylon.glTFFileLoader.js
  31. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  32. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  33. 5 0
      dist/preview release/loaders/babylonjs.loaders.d.ts
  34. 16 5
      dist/preview release/loaders/babylonjs.loaders.js
  35. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  36. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  37. 10 0
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  38. 3 3
      dist/preview release/loaders/package.json
  39. 2 2
      dist/preview release/materialsLibrary/package.json
  40. 2 2
      dist/preview release/nodeEditor/package.json
  41. 1 1
      dist/preview release/package.json
  42. 1 1
      dist/preview release/packagesSizeBaseLine.json
  43. 2 2
      dist/preview release/postProcessesLibrary/package.json
  44. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  45. 3 3
      dist/preview release/serializers/package.json
  46. 312 86
      dist/preview release/viewer/babylon.module.d.ts
  47. 24 20
      dist/preview release/viewer/babylon.viewer.js
  48. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  49. 10 0
      dist/preview release/viewer/babylonjs.loaders.module.d.ts
  50. 8 1
      dist/preview release/what's new.md
  51. 9 0
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  52. 49 0
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/followCameraPropertyGridComponent.tsx
  53. 4 1
      inspector/src/components/actionTabs/tabs/propertyGrids/postProcesses/defaultRenderingPipelinePropertyGridComponent.tsx
  54. 0 5
      inspector/src/components/actionTabs/tabs/propertyGrids/variantsPropertyGridComponent.tsx
  55. 17 5
      loaders/src/glTF/2.0/Extensions/KHR_materials_variants.ts
  56. 2 0
      loaders/src/glTF/glTFFileLoader.ts
  57. 1 1
      package.json
  58. 18 0
      src/Animations/animation.ts
  59. 150 108
      src/Audio/sound.ts
  60. 14 2
      src/Cameras/Inputs/freeCameraTouchInput.ts
  61. 2 0
      src/Cameras/targetCamera.ts
  62. 3 0
      src/Cameras/touchCamera.ts
  63. 21 2
      src/Engines/thinEngine.ts
  64. 3 0
      src/Gizmos/axisDragGizmo.ts
  65. 0 5
      src/Gizmos/lightGizmo.ts
  66. 67 0
      src/LibDeclarations/NativeExtensions/FeaturePoints.md
  67. 65 59
      src/LibDeclarations/webxr.d.ts
  68. 11 0
      src/LibDeclarations/webxr.nativeextensions.d.ts
  69. 2 0
      src/Lights/Shadows/cascadedShadowGenerator.ts
  70. 10 7
      src/Meshes/Builders/sphereBuilder.ts
  71. 1 1
      src/Meshes/thinInstanceMesh.ts
  72. 3 2
      src/Misc/screenshotTools.ts
  73. 1 0
      src/Rendering/outlineRenderer.ts
  74. 2 0
      src/Sprites/spriteManager.ts
  75. 4 0
      src/XR/features/WebXRControllerPointerSelection.ts
  76. 1 1
      src/XR/features/WebXRControllerTeleportation.ts
  77. 170 0
      src/XR/features/WebXRFeaturePointSystem.ts
  78. 366 0
      src/XR/features/WebXRHandTracking.ts
  79. 2 0
      src/XR/features/index.ts
  80. 6 6
      src/XR/motionController/webXRAbstractMotionController.ts
  81. 6 3
      src/XR/motionController/webXRProfiledMotionController.ts
  82. 11 7
      src/XR/webXREnterExitUI.ts
  83. 26 7
      src/XR/webXRFeaturesManager.ts
  84. 6 0
      src/XR/webXRInputSource.ts
  85. 2 2
      src/XR/webXRManagedOutputCanvas.ts
  86. 1 1
      src/XR/webXRSessionManager.ts

+ 57 - 52
Playground/src/components/examplesComponent.tsx

@@ -1,5 +1,5 @@
 import * as React from "react";
-import { GlobalState } from '../globalState';
+import { GlobalState } from "../globalState";
 
 require("../scss/examples.scss");
 
@@ -7,8 +7,8 @@ interface IExamplesComponentProps {
     globalState: GlobalState;
 }
 
-export class ExamplesComponent extends React.Component<IExamplesComponentProps, {filter: string}> {  
-    private _state = "";
+export class ExamplesComponent extends React.Component<IExamplesComponentProps, { filter: string }> {
+    private _state = "removed";
     private _rootRef: React.RefObject<HTMLDivElement>;
     private _scripts: {
         title: string;
@@ -19,33 +19,39 @@ export class ExamplesComponent extends React.Component<IExamplesComponentProps,
             PGID: string;
             description: string;
         }[];
-    }[];  
-  
+    }[];
+
     public constructor(props: IExamplesComponentProps) {
         super(props);
         this._loadScripts();
 
-        this.state = {filter: ""};
+        this.state = { filter: "" };
         this._rootRef = React.createRef();
 
         this.props.globalState.onExamplesDisplayChangedObservable.add(() => {
-            if (this._state === "") {
-                this._rootRef.current!.classList.add("visible");
-                this._state = "visible";
+            if (this._state !== "visible") {
+                this._rootRef.current!.classList.remove("removed");
+                setTimeout(() => {
+                    this._rootRef.current!.classList.add("visible");
+                    this._state = "visible";
+                }, 16);
             } else {
                 this._rootRef.current!.classList.remove("visible");
                 this._state = "";
+                setTimeout(() => {
+                    this._rootRef.current!.classList.add("removed");
+                }, 200);
             }
         });
-    }  
+    }
 
     private _loadScripts() {
         var xhr = new XMLHttpRequest();
 
         if (this.props.globalState.language === "JS") {
-            xhr.open('GET', 'https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list.json', true);
+            xhr.open("GET", "https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list.json", true);
         } else {
-            xhr.open('GET', 'https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list_ts.json', true);
+            xhr.open("GET", "https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list_ts.json", true);
         }
 
         xhr.onreadystatechange = () => {
@@ -60,7 +66,7 @@ export class ExamplesComponent extends React.Component<IExamplesComponentProps,
                         return 1;
                     });
 
-                    this._scripts.forEach(s => {
+                    this._scripts.forEach((s) => {
                         s.samples.sort((a, b) => {
                             if (a.title < b.title) {
                                 return -1;
@@ -72,12 +78,11 @@ export class ExamplesComponent extends React.Component<IExamplesComponentProps,
                     this.forceUpdate();
                 }
             }
-        }
+        };
 
         xhr.send(null);
     }
 
-
     private _onLoadPG(id: string) {
         this.props.globalState.onLoadRequiredObservable.notifyObservers(id);
 
@@ -95,46 +100,46 @@ export class ExamplesComponent extends React.Component<IExamplesComponentProps,
             <div id="examples" className={this._state} ref={this._rootRef}>
                 <div id="examples-header">Examples</div>
                 <div id="examples-filter">
-                    <input id="examples-filter-text" type="text" placeholder="Filter examples" value={this.state.filter} onChange={evt => {
-                        this.setState({filter: evt.target.value});
-                    }}/>
+                    <input
+                        id="examples-filter-text"
+                        type="text"
+                        placeholder="Filter examples"
+                        value={this.state.filter}
+                        onChange={(evt) => {
+                            this.setState({ filter: evt.target.value });
+                        }}
+                    />
                 </div>
                 <div id="examples-list">
-                    {
-                        this._scripts.map(s => {
-                            let active = s.samples.filter(ss => {
-                                return !this.state.filter 
-                                    || ss.title.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1
-                                    || ss.description.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1
-                            });
-
-                            if (active.length === 0) {
-                                return null;
-                            }
+                    {this._scripts.map((s) => {
+                        let active = s.samples.filter((ss) => {
+                            return !this.state.filter || ss.title.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1 || ss.description.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1;
+                        });
+
+                        if (active.length === 0) {
+                            return null;
+                        }
 
-                            return(
-                                <div key={s.title} className="example-category">
-                                    <div className="example-category-title">
-                                        {s.title}
-                                    </div>
-                                    {
-                                        active.map(ss => {
-                                            return (
-                                                <div className="example" key={ss.title} onClick={() => this._onLoadPG(ss.PGID)}>
-                                                    <img src={ss.icon.replace("icons", "https://doc.babylonjs.com/examples/icons")}/>
-                                                    <div className="example-title">{ss.title}</div>
-                                                    <div className="example-description">{ss.description}</div>
-                                                    <a className="example-link" href={ss.doc} target="_blank">Documentation</a>
-                                                </div>
-                                            )
-                                        })
-                                    }
-                                </div>
-                            )
-                        })
-                    }
+                        return (
+                            <div key={s.title} className="example-category">
+                                <div className="example-category-title">{s.title}</div>
+                                {active.map((ss) => {
+                                    return (
+                                        <div className="example" key={ss.title} onClick={() => this._onLoadPG(ss.PGID)}>
+                                            <img src={ss.icon.replace("icons", "https://doc.babylonjs.com/examples/icons")} />
+                                            <div className="example-title">{ss.title}</div>
+                                            <div className="example-description">{ss.description}</div>
+                                            <a className="example-link" href={ss.doc} target="_blank">
+                                                Documentation
+                                            </a>
+                                        </div>
+                                    );
+                                })}
+                            </div>
+                        );
+                    })}
                 </div>
             </div>
-        )
+        );
     }
-}
+}

+ 4 - 0
Playground/src/scss/examples.scss

@@ -16,6 +16,10 @@
         opacity: 1;
     }
 
+    &.removed {
+        display: none;
+    }
+
     width: 380px;
     display: grid;
     grid-template-columns: 100%;

+ 24 - 18
Playground/src/tools/loadManager.ts

@@ -1,22 +1,22 @@
-import { GlobalState } from '../globalState';
-import { Utilities } from './utilities';
+import { GlobalState } from "../globalState";
+import { Utilities } from "./utilities";
 
 export class LoadManager {
     private _previousHash = "";
 
-    public constructor(public globalState: GlobalState) {  
-        // Check the url to prepopulate data        
+    public constructor(public globalState: GlobalState) {
+        // Check the url to prepopulate data
         this._checkHash();
         window.addEventListener("hashchange", () => this._checkHash());
 
-        globalState.onLoadRequiredObservable.add(id => {
+        globalState.onLoadRequiredObservable.add((id) => {
             globalState.onDisplayWaitRingObservable.notifyObservers(true);
             this._loadPlayground(id);
         });
     }
 
     private _cleanHash() {
-        var substr = location.hash[1]==='#' ? 2 : 1
+        var substr = location.hash[1] === "#" ? 2 : 1;
         var splits = decodeURIComponent(location.hash.substr(substr)).split("#");
 
         if (splits.length > 2) {
@@ -24,14 +24,14 @@ export class LoadManager {
         }
 
         location.hash = splits.join("#");
-    };
+    }
 
     private _checkHash() {
         let pgHash = "";
-        if (location.search && (!location.pathname  || location.pathname === '/') && !location.hash) {
+        if (location.search && (!location.pathname || location.pathname === "/") && !location.hash) {
             var query = Utilities.ParseQuery();
             if (query.pg) {
-                pgHash = "#" + query.pg + "#" + (query.revision || "0")
+                pgHash = "#" + query.pg + "#" + (query.revision || "0");
             }
         } else if (location.hash) {
             if (this._previousHash !== location.hash) {
@@ -52,27 +52,27 @@ export class LoadManager {
         if (pgHash) {
             var match = pgHash.match(/^(#[A-Za-z\d]*)(%23)([\d]+)$/);
             if (match) {
-                pgHash = match[1] + '#' + match[3];
+                pgHash = match[1] + "#" + match[3];
                 parent.location.hash = pgHash;
             }
             this._previousHash = pgHash;
             this._loadPlayground(pgHash.substr(1));
-        }        
+        }
     }
 
-    private _loadPlayground(id: string) {        
+    private _loadPlayground(id: string) {
         this.globalState.loadingCodeInProgress = true;
         try {
             var xmlHttp = new XMLHttpRequest();
             xmlHttp.onreadystatechange = () => {
                 if (xmlHttp.readyState === 4) {
                     if (xmlHttp.status === 200) {
-
                         if (xmlHttp.responseText.indexOf("class Playground") !== -1) {
                             if (this.globalState.language === "JS") {
                                 Utilities.SwitchLanguage("TS", this.globalState);
                             }
-                        } else { // If we're loading JS content and it's TS page
+                        } else {
+                            // If we're loading JS content and it's TS page
                             if (this.globalState.language === "TS") {
                                 Utilities.SwitchLanguage("JS", this.globalState);
                             }
@@ -100,20 +100,26 @@ export class LoadManager {
                         }
 
                         this.globalState.onCodeLoaded.notifyObservers(JSON.parse(snippet.jsonPayload).code.toString());
-                         
+
                         this.globalState.onMetadataUpdatedObservable.notifyObservers();
                     }
                 }
+            };
+
+            if (id[0] === "#") {
+                id = id.substr(1);
             }
 
             this.globalState.currentSnippetToken = id.split("#")[0];
-            if (!id.split("#")[1]) id += "#0";
+            if (!id.split("#")[1]) {
+                id += "#0";
+            }
 
-            xmlHttp.open("GET", this.globalState.SnippetServerUrl + "/" + id.replace("#", "/"));
+            xmlHttp.open("GET", this.globalState.SnippetServerUrl + "/" + id.replace(/#/g, "/"));
             xmlHttp.send();
         } catch (e) {
             this.globalState.loadingCodeInProgress = false;
             this.globalState.onCodeLoaded.notifyObservers("");
         }
     }
-}
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 22 - 0
dist/ktx2Transcoders/msc_basis_transcoder.js


BIN=BIN
dist/ktx2Transcoders/msc_basis_transcoder.wasm


BIN=BIN
dist/ktx2Transcoders/uastc_astc.wasm


BIN=BIN
dist/ktx2Transcoders/uastc_bc7.wasm


+ 159 - 43
dist/preview release/babylon.d.ts

@@ -18861,6 +18861,10 @@ declare module BABYLON {
      */
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse: boolean;
+        /**
          * Defines the camera the input is attached to.
          */
         camera: FreeCamera;
@@ -18881,6 +18885,16 @@ declare module BABYLON {
         private _observer;
         private _onLostFocus;
         /**
+         * Manage the touch inputs to control the movement of a free camera.
+         * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+         * @param allowMouse Defines if mouse events can be treated as touch events
+         */
+        constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse?: boolean);
+        /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
          * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -35154,6 +35168,15 @@ declare module BABYLON {
         _removePendingData(data: any): void;
         offlineProvider: IOfflineProvider;
     }
+    /**
+     * Information about the current host
+     */
+    export interface HostInformation {
+        /**
+         * Defines if the current host is a mobile
+         */
+        isMobile: boolean;
+    }
     /** Interface defining initialization parameters for Engine class */
     export interface EngineOptions extends WebGLContextAttributes {
         /**
@@ -35409,6 +35432,10 @@ declare module BABYLON {
         private _activeRequests;
         /** @hidden */
         _transformTextureUrl: Nullable<(url: string) => string>;
+        /**
+         * Gets information about the current host
+         */
+        hostInformation: HostInformation;
         protected get _supportsHardwareTextureRescaling(): boolean;
         private _framebufferDimensionsObject;
         /**
@@ -41383,8 +41410,8 @@ declare module BABYLON {
      */
     export interface ISoundOptions {
         /**
-        * Does the sound autoplay once loaded.
-        */
+         * Does the sound autoplay once loaded.
+         */
         autoplay?: boolean;
         /**
          * Does the sound loop after it finishes playing once.
@@ -41407,9 +41434,9 @@ declare module BABYLON {
          */
         useCustomAttenuation?: boolean;
         /**
-        * Define the roll off factor of spatial sounds.
-        * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-        */
+         * Define the roll off factor of spatial sounds.
+         * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+         */
         rolloffFactor?: number;
         /**
          * Define the reference distance the sound should be heard perfectly.
@@ -41551,13 +41578,13 @@ declare module BABYLON {
         /** @hidden */
         static _SceneComponentInitialization: (scene: Scene) => void;
         /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-        * @param scene defines the scene the sound belongs to
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
+         * Create a sound and attach it to a scene
+         * @param name Name of your sound
+         * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+         * @param scene defines the scene the sound belongs to
+         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+         */
         constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: Nullable<() => void>, options?: ISoundOptions);
         /**
          * Release the sound and its associated resources
@@ -41600,11 +41627,11 @@ declare module BABYLON {
          */
         connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
         /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
+         * Transform this sound into a directional source
+         * @param coneInnerAngle Size of the inner cone in degree
+         * @param coneOuterAngle Size of the outer cone in degree
+         * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+         */
         setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
         /**
          * Gets or sets the inner angle for the directional cone.
@@ -41642,17 +41669,17 @@ declare module BABYLON {
          */
         setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
         /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        * @param offset (optional) Start the sound at a specific time in seconds
-        * @param length (optional) Sound duration (in seconds)
-        */
+         * Play the sound
+         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+         * @param offset (optional) Start the sound at a specific time in seconds
+         * @param length (optional) Sound duration (in seconds)
+         */
         play(time?: number, offset?: number, length?: number): void;
         private _onended;
         /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
+         * Stop the sound
+         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+         */
         stop(time?: number): void;
         /**
          * Put the sound in pause
@@ -47063,31 +47090,35 @@ declare module BABYLON {
         /**
          * The name of the anchor system feature
          */
-        static ANCHOR_SYSTEM: string;
+        static readonly ANCHOR_SYSTEM: string;
         /**
          * The name of the background remover feature
          */
-        static BACKGROUND_REMOVER: string;
+        static readonly BACKGROUND_REMOVER: string;
         /**
          * The name of the hit test feature
          */
-        static HIT_TEST: string;
+        static readonly HIT_TEST: string;
         /**
          * physics impostors for xr controllers feature
          */
-        static PHYSICS_CONTROLLERS: string;
+        static readonly PHYSICS_CONTROLLERS: string;
         /**
          * The name of the plane detection feature
          */
-        static PLANE_DETECTION: string;
+        static readonly PLANE_DETECTION: string;
         /**
          * The name of the pointer selection feature
          */
-        static POINTER_SELECTION: string;
+        static readonly POINTER_SELECTION: string;
         /**
          * The name of the teleportation feature
          */
-        static TELEPORTATION: string;
+        static readonly TELEPORTATION: string;
+        /**
+         * The name of the feature points feature.
+         */
+        static readonly FEATURE_POINTS: string;
     }
     /**
      * Defining the constructor of a feature. Used to register the modules.
@@ -48800,7 +48831,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -49327,7 +49358,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -51394,13 +51425,13 @@ declare module BABYLON {
          * @returns Current value of input
          */
         /**
-         * Checks for current device input value, given an id and input index
+         * Checks for current device input value, given an id and input index. Throws exception if requested device not initialized.
          * @param deviceType Enum specifiying device type
          * @param deviceSlot "Slot" or index that device is referenced in
          * @param inputIndex Id of input to be checked
          * @returns Current value of input
          */
-        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): number;
         /**
          * Dispose of all the eventlisteners
          */
@@ -73869,8 +73900,9 @@ declare module BABYLON {
          * @param antialiasing Whether antialiasing should be turned on or not (default: false)
          * @param fileName A name for for the downloaded file.
          * @param renderSprites Whether the sprites should be rendered or not (default: false)
+         * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
          */
-        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean): void;
+        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean, enableStencilBuffer?: boolean): void;
         /**
          * Generates an image screenshot from the specified camera.
          * @see https://doc.babylonjs.com/how_to/render_scene_on_a_png
@@ -74096,7 +74128,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74238,7 +74270,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74340,7 +74372,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74471,7 +74503,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74557,7 +74589,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74687,7 +74719,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74756,6 +74788,79 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * A babylon interface for a "WebXR" feature point.
+     * Represents the position and confidence value of a given feature point.
+     */
+    export interface IWebXRFeaturePoint {
+        /**
+         * Represents the position of the feature point in world space.
+         */
+        position: Vector3;
+        /**
+         * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+         */
+        confidenceValue: number;
+    }
+    /**
+     * The feature point system is used to detect feature points from real world geometry.
+     * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+     * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+     */
+    export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+        private _enabled;
+        private _featurePointCloud;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the WebXR specs version
+         */
+        static readonly Version: number;
+        /**
+        * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+        * Will notify the observers about which feature points have been added.
+        */
+        readonly onFeaturePointsAddedObservable: Observable<number[]>;
+        /**
+         * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+         * Will notify the observers about which feature points have been updated.
+         */
+        readonly onFeaturePointsUpdatedObservable: Observable<number[]>;
+        /**
+         * The current feature point cloud maintained across frames.
+         */
+        get featurePointCloud(): Array<IWebXRFeaturePoint>;
+        /**
+         * construct the feature point system
+         * @param _xrSessionManager an instance of xr Session manager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        /**
+         * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+         */
+        protected _onXRFrame(frame: XRFrame): void;
+        /**
+         * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+         */
+        private _init;
+    }
+}
+declare module BABYLON {
+    /**
      * The motion controller class for all microsoft mixed reality controllers
      */
     export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionController {
@@ -75720,4 +75825,15 @@ interface XRPlane {
     planeSpace: XRSpace;
     polygon: Array<DOMPointReadOnly>;
     lastChangedTime: number;
+}
+// This file contains native only extensions for WebXR  These APIs are not supported in the browser yet.
+// They are intended for use with either Babylon Native https://github.com/BabylonJS/BabylonNative or
+// Babylon React Native: https://github.com/BabylonJS/BabylonReactNative
+
+interface XRSession {
+    trySetFeaturePointCloudEnabled(enabled: boolean): boolean;
+}
+
+interface XRFrame {
+    featurePointCloud? : Array<number>;
 }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 2
dist/preview release/babylon.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 326 - 64
dist/preview release/babylon.max.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 312 - 86
dist/preview release/babylon.module.d.ts

@@ -19408,6 +19408,10 @@ declare module "babylonjs/Cameras/Inputs/freeCameraTouchInput" {
      */
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse: boolean;
+        /**
          * Defines the camera the input is attached to.
          */
         camera: FreeCamera;
@@ -19428,6 +19432,16 @@ declare module "babylonjs/Cameras/Inputs/freeCameraTouchInput" {
         private _observer;
         private _onLostFocus;
         /**
+         * Manage the touch inputs to control the movement of a free camera.
+         * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+         * @param allowMouse Defines if mouse events can be treated as touch events
+         */
+        constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse?: boolean);
+        /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
          * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -36414,6 +36428,15 @@ declare module "babylonjs/Engines/thinEngine" {
         _removePendingData(data: any): void;
         offlineProvider: IOfflineProvider;
     }
+    /**
+     * Information about the current host
+     */
+    export interface HostInformation {
+        /**
+         * Defines if the current host is a mobile
+         */
+        isMobile: boolean;
+    }
     /** Interface defining initialization parameters for Engine class */
     export interface EngineOptions extends WebGLContextAttributes {
         /**
@@ -36669,6 +36692,10 @@ declare module "babylonjs/Engines/thinEngine" {
         private _activeRequests;
         /** @hidden */
         _transformTextureUrl: Nullable<(url: string) => string>;
+        /**
+         * Gets information about the current host
+         */
+        hostInformation: HostInformation;
         protected get _supportsHardwareTextureRescaling(): boolean;
         private _framebufferDimensionsObject;
         /**
@@ -42793,8 +42820,8 @@ declare module "babylonjs/Audio/sound" {
      */
     export interface ISoundOptions {
         /**
-        * Does the sound autoplay once loaded.
-        */
+         * Does the sound autoplay once loaded.
+         */
         autoplay?: boolean;
         /**
          * Does the sound loop after it finishes playing once.
@@ -42817,9 +42844,9 @@ declare module "babylonjs/Audio/sound" {
          */
         useCustomAttenuation?: boolean;
         /**
-        * Define the roll off factor of spatial sounds.
-        * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-        */
+         * Define the roll off factor of spatial sounds.
+         * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+         */
         rolloffFactor?: number;
         /**
          * Define the reference distance the sound should be heard perfectly.
@@ -42961,13 +42988,13 @@ declare module "babylonjs/Audio/sound" {
         /** @hidden */
         static _SceneComponentInitialization: (scene: Scene) => void;
         /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-        * @param scene defines the scene the sound belongs to
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
+         * Create a sound and attach it to a scene
+         * @param name Name of your sound
+         * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+         * @param scene defines the scene the sound belongs to
+         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+         */
         constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: Nullable<() => void>, options?: ISoundOptions);
         /**
          * Release the sound and its associated resources
@@ -43010,11 +43037,11 @@ declare module "babylonjs/Audio/sound" {
          */
         connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
         /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
+         * Transform this sound into a directional source
+         * @param coneInnerAngle Size of the inner cone in degree
+         * @param coneOuterAngle Size of the outer cone in degree
+         * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+         */
         setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
         /**
          * Gets or sets the inner angle for the directional cone.
@@ -43052,17 +43079,17 @@ declare module "babylonjs/Audio/sound" {
          */
         setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
         /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        * @param offset (optional) Start the sound at a specific time in seconds
-        * @param length (optional) Sound duration (in seconds)
-        */
+         * Play the sound
+         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+         * @param offset (optional) Start the sound at a specific time in seconds
+         * @param length (optional) Sound duration (in seconds)
+         */
         play(time?: number, offset?: number, length?: number): void;
         private _onended;
         /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
+         * Stop the sound
+         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+         */
         stop(time?: number): void;
         /**
          * Put the sound in pause
@@ -48841,31 +48868,35 @@ declare module "babylonjs/XR/webXRFeaturesManager" {
         /**
          * The name of the anchor system feature
          */
-        static ANCHOR_SYSTEM: string;
+        static readonly ANCHOR_SYSTEM: string;
         /**
          * The name of the background remover feature
          */
-        static BACKGROUND_REMOVER: string;
+        static readonly BACKGROUND_REMOVER: string;
         /**
          * The name of the hit test feature
          */
-        static HIT_TEST: string;
+        static readonly HIT_TEST: string;
         /**
          * physics impostors for xr controllers feature
          */
-        static PHYSICS_CONTROLLERS: string;
+        static readonly PHYSICS_CONTROLLERS: string;
         /**
          * The name of the plane detection feature
          */
-        static PLANE_DETECTION: string;
+        static readonly PLANE_DETECTION: string;
         /**
          * The name of the pointer selection feature
          */
-        static POINTER_SELECTION: string;
+        static readonly POINTER_SELECTION: string;
         /**
          * The name of the teleportation feature
          */
-        static TELEPORTATION: string;
+        static readonly TELEPORTATION: string;
+        /**
+         * The name of the feature points feature.
+         */
+        static readonly FEATURE_POINTS: string;
     }
     /**
      * Defining the constructor of a feature. Used to register the modules.
@@ -50643,7 +50674,7 @@ declare module "babylonjs/XR/features/WebXRControllerPointerSelection" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -51191,7 +51222,7 @@ declare module "babylonjs/XR/features/WebXRControllerTeleportation" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -53466,13 +53497,13 @@ declare module "babylonjs/DeviceInput/deviceInputSystem" {
          * @returns Current value of input
          */
         /**
-         * Checks for current device input value, given an id and input index
+         * Checks for current device input value, given an id and input index. Throws exception if requested device not initialized.
          * @param deviceType Enum specifiying device type
          * @param deviceSlot "Slot" or index that device is referenced in
          * @param inputIndex Id of input to be checked
          * @returns Current value of input
          */
-        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): number;
         /**
          * Dispose of all the eventlisteners
          */
@@ -77949,8 +77980,9 @@ declare module "babylonjs/Misc/screenshotTools" {
          * @param antialiasing Whether antialiasing should be turned on or not (default: false)
          * @param fileName A name for for the downloaded file.
          * @param renderSprites Whether the sprites should be rendered or not (default: false)
+         * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
          */
-        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean): void;
+        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean, enableStencilBuffer?: boolean): void;
         /**
          * Generates an image screenshot from the specified camera.
          * @see https://doc.babylonjs.com/how_to/render_scene_on_a_png
@@ -78236,7 +78268,7 @@ declare module "babylonjs/XR/features/WebXRHitTestLegacy" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78383,7 +78415,7 @@ declare module "babylonjs/XR/features/WebXRHitTest" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78491,7 +78523,7 @@ declare module "babylonjs/XR/features/WebXRAnchorSystem" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78627,7 +78659,7 @@ declare module "babylonjs/XR/features/WebXRPlaneDetector" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78717,7 +78749,7 @@ declare module "babylonjs/XR/features/WebXRBackgroundRemover" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78853,7 +78885,7 @@ declare module "babylonjs/XR/features/WebXRControllerPhysics" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78920,6 +78952,83 @@ declare module "babylonjs/XR/features/WebXRControllerPhysics" {
         private _detachController;
     }
 }
+declare module "babylonjs/XR/features/WebXRFeaturePointSystem" {
+    import { WebXRSessionManager } from "babylonjs/XR/webXRSessionManager";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { Vector3 } from "babylonjs/Maths/math.vector";
+    import { WebXRAbstractFeature } from "babylonjs/XR/features/WebXRAbstractFeature";
+    /**
+     * A babylon interface for a "WebXR" feature point.
+     * Represents the position and confidence value of a given feature point.
+     */
+    export interface IWebXRFeaturePoint {
+        /**
+         * Represents the position of the feature point in world space.
+         */
+        position: Vector3;
+        /**
+         * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+         */
+        confidenceValue: number;
+    }
+    /**
+     * The feature point system is used to detect feature points from real world geometry.
+     * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+     * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+     */
+    export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+        private _enabled;
+        private _featurePointCloud;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the WebXR specs version
+         */
+        static readonly Version: number;
+        /**
+        * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+        * Will notify the observers about which feature points have been added.
+        */
+        readonly onFeaturePointsAddedObservable: Observable<number[]>;
+        /**
+         * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+         * Will notify the observers about which feature points have been updated.
+         */
+        readonly onFeaturePointsUpdatedObservable: Observable<number[]>;
+        /**
+         * The current feature point cloud maintained across frames.
+         */
+        get featurePointCloud(): Array<IWebXRFeaturePoint>;
+        /**
+         * construct the feature point system
+         * @param _xrSessionManager an instance of xr Session manager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        /**
+         * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+         */
+        protected _onXRFrame(frame: XRFrame): void;
+        /**
+         * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+         */
+        private _init;
+    }
+}
 declare module "babylonjs/XR/features/index" {
     export * from "babylonjs/XR/features/WebXRHitTestLegacy";
     export * from "babylonjs/XR/features/WebXRAnchorSystem";
@@ -78929,6 +79038,7 @@ declare module "babylonjs/XR/features/index" {
     export * from "babylonjs/XR/features/WebXRControllerPointerSelection";
     export * from "babylonjs/XR/features/WebXRControllerPhysics";
     export * from "babylonjs/XR/features/WebXRHitTest";
+    export * from "babylonjs/XR/features/WebXRFeaturePointSystem";
 }
 declare module "babylonjs/XR/motionController/webXRMicrosoftMixedRealityController" {
     import { WebXRAbstractMotionController, IMinimalMotionControllerObject, MotionControllerHandedness } from "babylonjs/XR/motionController/webXRAbstractMotionController";
@@ -98134,6 +98244,10 @@ declare module BABYLON {
      */
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse: boolean;
+        /**
          * Defines the camera the input is attached to.
          */
         camera: FreeCamera;
@@ -98154,6 +98268,16 @@ declare module BABYLON {
         private _observer;
         private _onLostFocus;
         /**
+         * Manage the touch inputs to control the movement of a free camera.
+         * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+         * @param allowMouse Defines if mouse events can be treated as touch events
+         */
+        constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse?: boolean);
+        /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
          * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -114427,6 +114551,15 @@ declare module BABYLON {
         _removePendingData(data: any): void;
         offlineProvider: IOfflineProvider;
     }
+    /**
+     * Information about the current host
+     */
+    export interface HostInformation {
+        /**
+         * Defines if the current host is a mobile
+         */
+        isMobile: boolean;
+    }
     /** Interface defining initialization parameters for Engine class */
     export interface EngineOptions extends WebGLContextAttributes {
         /**
@@ -114682,6 +114815,10 @@ declare module BABYLON {
         private _activeRequests;
         /** @hidden */
         _transformTextureUrl: Nullable<(url: string) => string>;
+        /**
+         * Gets information about the current host
+         */
+        hostInformation: HostInformation;
         protected get _supportsHardwareTextureRescaling(): boolean;
         private _framebufferDimensionsObject;
         /**
@@ -120656,8 +120793,8 @@ declare module BABYLON {
      */
     export interface ISoundOptions {
         /**
-        * Does the sound autoplay once loaded.
-        */
+         * Does the sound autoplay once loaded.
+         */
         autoplay?: boolean;
         /**
          * Does the sound loop after it finishes playing once.
@@ -120680,9 +120817,9 @@ declare module BABYLON {
          */
         useCustomAttenuation?: boolean;
         /**
-        * Define the roll off factor of spatial sounds.
-        * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-        */
+         * Define the roll off factor of spatial sounds.
+         * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+         */
         rolloffFactor?: number;
         /**
          * Define the reference distance the sound should be heard perfectly.
@@ -120824,13 +120961,13 @@ declare module BABYLON {
         /** @hidden */
         static _SceneComponentInitialization: (scene: Scene) => void;
         /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-        * @param scene defines the scene the sound belongs to
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
+         * Create a sound and attach it to a scene
+         * @param name Name of your sound
+         * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+         * @param scene defines the scene the sound belongs to
+         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+         */
         constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: Nullable<() => void>, options?: ISoundOptions);
         /**
          * Release the sound and its associated resources
@@ -120873,11 +121010,11 @@ declare module BABYLON {
          */
         connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
         /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
+         * Transform this sound into a directional source
+         * @param coneInnerAngle Size of the inner cone in degree
+         * @param coneOuterAngle Size of the outer cone in degree
+         * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+         */
         setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
         /**
          * Gets or sets the inner angle for the directional cone.
@@ -120915,17 +121052,17 @@ declare module BABYLON {
          */
         setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
         /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        * @param offset (optional) Start the sound at a specific time in seconds
-        * @param length (optional) Sound duration (in seconds)
-        */
+         * Play the sound
+         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+         * @param offset (optional) Start the sound at a specific time in seconds
+         * @param length (optional) Sound duration (in seconds)
+         */
         play(time?: number, offset?: number, length?: number): void;
         private _onended;
         /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
+         * Stop the sound
+         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+         */
         stop(time?: number): void;
         /**
          * Put the sound in pause
@@ -126336,31 +126473,35 @@ declare module BABYLON {
         /**
          * The name of the anchor system feature
          */
-        static ANCHOR_SYSTEM: string;
+        static readonly ANCHOR_SYSTEM: string;
         /**
          * The name of the background remover feature
          */
-        static BACKGROUND_REMOVER: string;
+        static readonly BACKGROUND_REMOVER: string;
         /**
          * The name of the hit test feature
          */
-        static HIT_TEST: string;
+        static readonly HIT_TEST: string;
         /**
          * physics impostors for xr controllers feature
          */
-        static PHYSICS_CONTROLLERS: string;
+        static readonly PHYSICS_CONTROLLERS: string;
         /**
          * The name of the plane detection feature
          */
-        static PLANE_DETECTION: string;
+        static readonly PLANE_DETECTION: string;
         /**
          * The name of the pointer selection feature
          */
-        static POINTER_SELECTION: string;
+        static readonly POINTER_SELECTION: string;
         /**
          * The name of the teleportation feature
          */
-        static TELEPORTATION: string;
+        static readonly TELEPORTATION: string;
+        /**
+         * The name of the feature points feature.
+         */
+        static readonly FEATURE_POINTS: string;
     }
     /**
      * Defining the constructor of a feature. Used to register the modules.
@@ -128073,7 +128214,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -128600,7 +128741,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -130667,13 +130808,13 @@ declare module BABYLON {
          * @returns Current value of input
          */
         /**
-         * Checks for current device input value, given an id and input index
+         * Checks for current device input value, given an id and input index. Throws exception if requested device not initialized.
          * @param deviceType Enum specifiying device type
          * @param deviceSlot "Slot" or index that device is referenced in
          * @param inputIndex Id of input to be checked
          * @returns Current value of input
          */
-        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): number;
         /**
          * Dispose of all the eventlisteners
          */
@@ -153142,8 +153283,9 @@ declare module BABYLON {
          * @param antialiasing Whether antialiasing should be turned on or not (default: false)
          * @param fileName A name for for the downloaded file.
          * @param renderSprites Whether the sprites should be rendered or not (default: false)
+         * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
          */
-        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean): void;
+        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean, enableStencilBuffer?: boolean): void;
         /**
          * Generates an image screenshot from the specified camera.
          * @see https://doc.babylonjs.com/how_to/render_scene_on_a_png
@@ -153369,7 +153511,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153511,7 +153653,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153613,7 +153755,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153744,7 +153886,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153830,7 +153972,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153960,7 +154102,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -154029,6 +154171,79 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * A babylon interface for a "WebXR" feature point.
+     * Represents the position and confidence value of a given feature point.
+     */
+    export interface IWebXRFeaturePoint {
+        /**
+         * Represents the position of the feature point in world space.
+         */
+        position: Vector3;
+        /**
+         * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+         */
+        confidenceValue: number;
+    }
+    /**
+     * The feature point system is used to detect feature points from real world geometry.
+     * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+     * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+     */
+    export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+        private _enabled;
+        private _featurePointCloud;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the WebXR specs version
+         */
+        static readonly Version: number;
+        /**
+        * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+        * Will notify the observers about which feature points have been added.
+        */
+        readonly onFeaturePointsAddedObservable: Observable<number[]>;
+        /**
+         * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+         * Will notify the observers about which feature points have been updated.
+         */
+        readonly onFeaturePointsUpdatedObservable: Observable<number[]>;
+        /**
+         * The current feature point cloud maintained across frames.
+         */
+        get featurePointCloud(): Array<IWebXRFeaturePoint>;
+        /**
+         * construct the feature point system
+         * @param _xrSessionManager an instance of xr Session manager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        /**
+         * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+         */
+        protected _onXRFrame(frame: XRFrame): void;
+        /**
+         * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+         */
+        private _init;
+    }
+}
+declare module BABYLON {
+    /**
      * The motion controller class for all microsoft mixed reality controllers
      */
     export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionController {
@@ -154993,4 +155208,15 @@ interface XRPlane {
     planeSpace: XRSpace;
     polygon: Array<DOMPointReadOnly>;
     lastChangedTime: number;
+}
+// This file contains native only extensions for WebXR  These APIs are not supported in the browser yet.
+// They are intended for use with either Babylon Native https://github.com/BabylonJS/BabylonNative or
+// Babylon React Native: https://github.com/BabylonJS/BabylonReactNative
+
+interface XRSession {
+    trySetFeaturePointCloudEnabled(enabled: boolean): boolean;
+}
+
+interface XRFrame {
+    featurePointCloud? : Array<number>;
 }

+ 176 - 48
dist/preview release/documentation.d.ts

@@ -18861,6 +18861,10 @@ declare module BABYLON {
      */
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse: boolean;
+        /**
          * Defines the camera the input is attached to.
          */
         camera: FreeCamera;
@@ -18881,6 +18885,16 @@ declare module BABYLON {
         private _observer;
         private _onLostFocus;
         /**
+         * Manage the touch inputs to control the movement of a free camera.
+         * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+         * @param allowMouse Defines if mouse events can be treated as touch events
+         */
+        constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse?: boolean);
+        /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
          * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -35154,6 +35168,15 @@ declare module BABYLON {
         _removePendingData(data: any): void;
         offlineProvider: IOfflineProvider;
     }
+    /**
+     * Information about the current host
+     */
+    export interface HostInformation {
+        /**
+         * Defines if the current host is a mobile
+         */
+        isMobile: boolean;
+    }
     /** Interface defining initialization parameters for Engine class */
     export interface EngineOptions extends WebGLContextAttributes {
         /**
@@ -35409,6 +35432,10 @@ declare module BABYLON {
         private _activeRequests;
         /** @hidden */
         _transformTextureUrl: Nullable<(url: string) => string>;
+        /**
+         * Gets information about the current host
+         */
+        hostInformation: HostInformation;
         protected get _supportsHardwareTextureRescaling(): boolean;
         private _framebufferDimensionsObject;
         /**
@@ -41383,8 +41410,8 @@ declare module BABYLON {
      */
     export interface ISoundOptions {
         /**
-        * Does the sound autoplay once loaded.
-        */
+         * Does the sound autoplay once loaded.
+         */
         autoplay?: boolean;
         /**
          * Does the sound loop after it finishes playing once.
@@ -41407,9 +41434,9 @@ declare module BABYLON {
          */
         useCustomAttenuation?: boolean;
         /**
-        * Define the roll off factor of spatial sounds.
-        * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-        */
+         * Define the roll off factor of spatial sounds.
+         * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+         */
         rolloffFactor?: number;
         /**
          * Define the reference distance the sound should be heard perfectly.
@@ -41551,13 +41578,13 @@ declare module BABYLON {
         /** @hidden */
         static _SceneComponentInitialization: (scene: Scene) => void;
         /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-        * @param scene defines the scene the sound belongs to
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
+         * Create a sound and attach it to a scene
+         * @param name Name of your sound
+         * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+         * @param scene defines the scene the sound belongs to
+         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+         */
         constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: Nullable<() => void>, options?: ISoundOptions);
         /**
          * Release the sound and its associated resources
@@ -41600,11 +41627,11 @@ declare module BABYLON {
          */
         connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
         /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
+         * Transform this sound into a directional source
+         * @param coneInnerAngle Size of the inner cone in degree
+         * @param coneOuterAngle Size of the outer cone in degree
+         * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+         */
         setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
         /**
          * Gets or sets the inner angle for the directional cone.
@@ -41642,17 +41669,17 @@ declare module BABYLON {
          */
         setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
         /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        * @param offset (optional) Start the sound at a specific time in seconds
-        * @param length (optional) Sound duration (in seconds)
-        */
+         * Play the sound
+         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+         * @param offset (optional) Start the sound at a specific time in seconds
+         * @param length (optional) Sound duration (in seconds)
+         */
         play(time?: number, offset?: number, length?: number): void;
         private _onended;
         /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
+         * Stop the sound
+         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+         */
         stop(time?: number): void;
         /**
          * Put the sound in pause
@@ -47063,31 +47090,35 @@ declare module BABYLON {
         /**
          * The name of the anchor system feature
          */
-        static ANCHOR_SYSTEM: string;
+        static readonly ANCHOR_SYSTEM: string;
         /**
          * The name of the background remover feature
          */
-        static BACKGROUND_REMOVER: string;
+        static readonly BACKGROUND_REMOVER: string;
         /**
          * The name of the hit test feature
          */
-        static HIT_TEST: string;
+        static readonly HIT_TEST: string;
         /**
          * physics impostors for xr controllers feature
          */
-        static PHYSICS_CONTROLLERS: string;
+        static readonly PHYSICS_CONTROLLERS: string;
         /**
          * The name of the plane detection feature
          */
-        static PLANE_DETECTION: string;
+        static readonly PLANE_DETECTION: string;
         /**
          * The name of the pointer selection feature
          */
-        static POINTER_SELECTION: string;
+        static readonly POINTER_SELECTION: string;
         /**
          * The name of the teleportation feature
          */
-        static TELEPORTATION: string;
+        static readonly TELEPORTATION: string;
+        /**
+         * The name of the feature points feature.
+         */
+        static readonly FEATURE_POINTS: string;
     }
     /**
      * Defining the constructor of a feature. Used to register the modules.
@@ -48800,7 +48831,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -49327,7 +49358,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -51394,13 +51425,13 @@ declare module BABYLON {
          * @returns Current value of input
          */
         /**
-         * Checks for current device input value, given an id and input index
+         * Checks for current device input value, given an id and input index. Throws exception if requested device not initialized.
          * @param deviceType Enum specifiying device type
          * @param deviceSlot "Slot" or index that device is referenced in
          * @param inputIndex Id of input to be checked
          * @returns Current value of input
          */
-        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): number;
         /**
          * Dispose of all the eventlisteners
          */
@@ -73869,8 +73900,9 @@ declare module BABYLON {
          * @param antialiasing Whether antialiasing should be turned on or not (default: false)
          * @param fileName A name for for the downloaded file.
          * @param renderSprites Whether the sprites should be rendered or not (default: false)
+         * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
          */
-        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean): void;
+        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean, enableStencilBuffer?: boolean): void;
         /**
          * Generates an image screenshot from the specified camera.
          * @see https://doc.babylonjs.com/how_to/render_scene_on_a_png
@@ -74096,7 +74128,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74238,7 +74270,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74340,7 +74372,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74471,7 +74503,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74557,7 +74589,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74687,7 +74719,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -74756,6 +74788,79 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * A babylon interface for a "WebXR" feature point.
+     * Represents the position and confidence value of a given feature point.
+     */
+    export interface IWebXRFeaturePoint {
+        /**
+         * Represents the position of the feature point in world space.
+         */
+        position: Vector3;
+        /**
+         * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+         */
+        confidenceValue: number;
+    }
+    /**
+     * The feature point system is used to detect feature points from real world geometry.
+     * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+     * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+     */
+    export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+        private _enabled;
+        private _featurePointCloud;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the WebXR specs version
+         */
+        static readonly Version: number;
+        /**
+        * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+        * Will notify the observers about which feature points have been added.
+        */
+        readonly onFeaturePointsAddedObservable: Observable<number[]>;
+        /**
+         * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+         * Will notify the observers about which feature points have been updated.
+         */
+        readonly onFeaturePointsUpdatedObservable: Observable<number[]>;
+        /**
+         * The current feature point cloud maintained across frames.
+         */
+        get featurePointCloud(): Array<IWebXRFeaturePoint>;
+        /**
+         * construct the feature point system
+         * @param _xrSessionManager an instance of xr Session manager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        /**
+         * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+         */
+        protected _onXRFrame(frame: XRFrame): void;
+        /**
+         * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+         */
+        private _init;
+    }
+}
+declare module BABYLON {
+    /**
      * The motion controller class for all microsoft mixed reality controllers
      */
     export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionController {
@@ -75721,6 +75826,17 @@ interface XRPlane {
     polygon: Array<DOMPointReadOnly>;
     lastChangedTime: number;
 }
+// This file contains native only extensions for WebXR  These APIs are not supported in the browser yet.
+// They are intended for use with either Babylon Native https://github.com/BabylonJS/BabylonNative or
+// Babylon React Native: https://github.com/BabylonJS/BabylonReactNative
+
+interface XRSession {
+    trySetFeaturePointCloudEnabled(enabled: boolean): boolean;
+}
+
+interface XRFrame {
+    featurePointCloud? : Array<number>;
+}
 
 /**
  * @ignore
@@ -80050,11 +80166,13 @@ declare module BABYLON {
         preprocessUrlAsync: (url: string) => Promise<string>;
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         private _onMeshLoadedObserver;
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set onMeshLoaded(callback: (mesh: AbstractMesh) => void);
         /**
@@ -81808,6 +81926,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
          */
         enabled: boolean;
         private _loader;
+        private _variants?;
         /** @hidden */
         constructor(loader: GLTFLoader);
         /** @hidden */
@@ -81860,6 +81979,8 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         getLastSelectedVariant(rootMesh: Mesh): Nullable<string | string[]>;
         private static _GetExtensionMetadata;
         /** @hidden */
+        onLoading(): void;
+        /** @hidden */
         _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>>;
     }
 }
@@ -84705,14 +84826,21 @@ declare module BABYLON.GLTF2 {
      */
 
     /** @hidden */
-    interface IKHRMaterialVariants_Mapping {
-        variants: string[];
-        material: number;
+    interface IKHRMaterialVariants_Mapping extends IProperty {
+        mapping: Array<{
+            variants: number[];
+            material: number;
+        }>;
+    }
+
+    /** @hidden */
+    interface IKHRMaterialVariants_Variant extends IProperty {
+        name: string;
     }
 
     /** @hidden */
-    interface IKHRMaterialVariants {
-        mapping: IKHRMaterialVariants_Mapping[];
+    interface IKHRMaterialVariants_Variants extends IChildRootProperty {
+        variants: Array<IKHRMaterialVariants_Variant>;
     }
 
     /**

+ 12 - 5
dist/preview release/glTF2Interface/babylon.glTF2Interface.d.ts

@@ -1061,14 +1061,21 @@ declare module BABYLON.GLTF2 {
      */
 
     /** @hidden */
-    interface IKHRMaterialVariants_Mapping {
-        variants: string[];
-        material: number;
+    interface IKHRMaterialVariants_Mapping extends IProperty {
+        mapping: Array<{
+            variants: number[];
+            material: number;
+        }>;
     }
 
     /** @hidden */
-    interface IKHRMaterialVariants {
-        mapping: IKHRMaterialVariants_Mapping[];
+    interface IKHRMaterialVariants_Variant extends IProperty {
+        name: string;
+    }
+
+    /** @hidden */
+    interface IKHRMaterialVariants_Variants extends IChildRootProperty {
+        variants: Array<IKHRMaterialVariants_Variant>;
     }
 
     /**

+ 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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -7,7 +7,7 @@
 		exports["babylonjs-gui"] = factory(require("babylonjs"));
 	else
 		root["BABYLON"] = root["BABYLON"] || {}, root["BABYLON"]["GUI"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -400,7 +400,7 @@ module.exports = g;
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTextureInstrumentation", function() { return AdvancedDynamicTextureInstrumentation; });
-/* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/perfCounter */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/perfCounter */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -543,7 +543,7 @@ var AdvancedDynamicTextureInstrumentation = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTexture", function() { return AdvancedDynamicTexture; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _controls_container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./controls/container */ "./2D/controls/container.ts");
 /* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./style */ "./2D/style.ts");
@@ -1530,7 +1530,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _textBlock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./textBlock */ "./2D/controls/textBlock.ts");
 /* harmony import */ var _image__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./image */ "./2D/controls/image.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__);
 
 
@@ -1762,7 +1762,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Checkbox", function() { return Checkbox; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -1945,7 +1945,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return ColorPicker; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
@@ -3338,7 +3338,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container", function() { return Container; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -3753,7 +3753,7 @@ babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredTypes
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control", function() { return Control; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -5701,7 +5701,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DisplayGrid", function() { return DisplayGrid; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -5934,7 +5934,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -6031,7 +6031,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -6489,7 +6489,7 @@ babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__["_TypeStore"].RegisteredTypes[
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Image", function() { return Image; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 
@@ -7424,7 +7424,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputPassword", function() { return InputPassword; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -7463,7 +7463,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputText", function() { return InputText; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8476,7 +8476,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Line", function() { return Line; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8747,7 +8747,7 @@ babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].Registere
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLine", function() { return MultiLine; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _multiLinePoint__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../multiLinePoint */ "./2D/multiLinePoint.ts");
@@ -9017,7 +9017,7 @@ babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].Registe
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RadioButton", function() { return RadioButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -9224,7 +9224,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Rectangle", function() { return Rectangle; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -9374,7 +9374,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _scrollViewerWindow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./scrollViewerWindow */ "./2D/controls/scrollViewers/scrollViewerWindow.ts");
 /* harmony import */ var _sliders_scrollBar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../sliders/scrollBar */ "./2D/controls/sliders/scrollBar.ts");
 /* harmony import */ var _sliders_imageScrollBar__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../sliders/imageScrollBar */ "./2D/controls/sliders/imageScrollBar.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__);
 
 
@@ -10997,7 +10997,7 @@ var SelectionPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSlider", function() { return BaseSlider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -11331,7 +11331,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _baseSlider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseSlider */ "./2D/controls/sliders/baseSlider.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../measure */ "./2D/measure.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -11924,7 +11924,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Slider", function() { return Slider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _baseSlider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseSlider */ "./2D/controls/sliders/baseSlider.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -12179,7 +12179,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel", function() { return StackPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -12447,7 +12447,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextWrapping", function() { return TextWrapping; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextBlock", function() { return TextBlock; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -12910,7 +12910,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeyPropertySet", function() { return KeyPropertySet; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualKeyboard", function() { return VirtualKeyboard; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
 /* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./button */ "./2D/controls/button.ts");
@@ -13299,7 +13299,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector2WithInfo", function() { return Vector2WithInfo; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Matrix2D", function() { return Matrix2D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -13524,7 +13524,7 @@ var Matrix2D = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Measure", function() { return Measure; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 var tmpRect = [
@@ -13689,7 +13689,7 @@ var Measure = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLinePoint", function() { return MultiLinePoint; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -13832,7 +13832,7 @@ var MultiLinePoint = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Style", function() { return Style; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -14138,7 +14138,7 @@ var ValueAndUnit = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "XmlLoader", function() { return XmlLoader; });
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -14457,7 +14457,7 @@ var XmlLoader = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbstractButton3D", function() { return AbstractButton3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -14500,7 +14500,7 @@ var AbstractButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Button3D", function() { return Button3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _abstractButton3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./abstractButton3D */ "./3D/controls/abstractButton3D.ts");
 /* harmony import */ var _2D_advancedDynamicTexture__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../2D/advancedDynamicTexture */ "./2D/advancedDynamicTexture.ts");
@@ -14681,7 +14681,7 @@ var Button3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container3D", function() { return Container3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -14838,7 +14838,7 @@ var Container3D = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control3D", function() { return Control3D; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _vector3WithInfo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../vector3WithInfo */ "./3D/vector3WithInfo.ts");
 
@@ -15244,7 +15244,7 @@ var Control3D = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CylinderPanel", function() { return CylinderPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -15330,7 +15330,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HolographicButton", function() { return HolographicButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _button3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./button3D */ "./3D/controls/button3D.ts");
-/* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Materials/standardMaterial */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Materials/standardMaterial */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _materials_fluentMaterial__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../materials/fluentMaterial */ "./3D/materials/fluentMaterial.ts");
 /* harmony import */ var _2D_controls_stackPanel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../2D/controls/stackPanel */ "./2D/controls/stackPanel.ts");
@@ -15824,7 +15824,7 @@ var MeshButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlanePanel", function() { return PlanePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
@@ -15879,7 +15879,7 @@ var PlanePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScatterPanel", function() { return ScatterPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -16006,7 +16006,7 @@ var ScatterPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpherePanel", function() { return SpherePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -16092,7 +16092,7 @@ var SpherePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel3D", function() { return StackPanel3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -16217,7 +16217,7 @@ var StackPanel3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VolumeBasedPanel", function() { return VolumeBasedPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -16408,7 +16408,7 @@ var VolumeBasedPanel = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GUI3DManager", function() { return GUI3DManager; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _controls_container3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./controls/container3D */ "./3D/controls/container3D.ts");
 
@@ -16675,7 +16675,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterialDefines", function() { return FluentMaterialDefines; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterial", function() { return FluentMaterial; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _shaders_fluent_vertex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./shaders/fluent.vertex */ "./3D/materials/shaders/fluent.vertex.ts");
 /* harmony import */ var _shaders_fluent_fragment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./shaders/fluent.fragment */ "./3D/materials/shaders/fluent.fragment.ts");
@@ -16998,7 +16998,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentPixelShader", function() { return fluentPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentPixelShader';
@@ -17020,7 +17020,7 @@ var fluentPixelShader = { name: name, shader: shader };
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentVertexShader", function() { return fluentVertexShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentVertexShader';
@@ -17043,7 +17043,7 @@ var fluentVertexShader = { name: name, shader: shader };
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector3WithInfo", function() { return Vector3WithInfo; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -17345,14 +17345,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Maths/math.vector":
+/***/ "babylonjs/Misc/perfCounter":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__;
 
 /***/ })
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


+ 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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33"
     },
     "engines": {
         "node": "*"

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


+ 63 - 8
dist/preview release/inspector/babylon.inspector.bundle.max.js

@@ -62845,6 +62845,8 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _propertyGrids_sprites_spriteManagerPropertyGridComponent__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! ./propertyGrids/sprites/spriteManagerPropertyGridComponent */ "./components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent.tsx");
 /* harmony import */ var _propertyGrids_sprites_spritePropertyGridComponent__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! ./propertyGrids/sprites/spritePropertyGridComponent */ "./components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent.tsx");
 /* harmony import */ var _propertyGrids_animations_targetedAnimationPropertyGridComponent__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! ./propertyGrids/animations/targetedAnimationPropertyGridComponent */ "./components/actionTabs/tabs/propertyGrids/animations/targetedAnimationPropertyGridComponent.tsx");
+/* harmony import */ var _propertyGrids_cameras_followCameraPropertyGridComponent__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! ./propertyGrids/cameras/followCameraPropertyGridComponent */ "./components/actionTabs/tabs/propertyGrids/cameras/followCameraPropertyGridComponent.tsx");
+
 
 
 
@@ -62953,6 +62955,10 @@ var PropertyGridTabComponent = /** @class */ (function (_super) {
                 var arcRotateCamera = entity;
                 return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_propertyGrids_cameras_arcRotateCameraPropertyGridComponent__WEBPACK_IMPORTED_MODULE_11__["ArcRotateCameraPropertyGridComponent"], { globalState: this.props.globalState, camera: arcRotateCamera, lockObject: this._lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }));
             }
+            if (className.indexOf("FollowCamera") !== -1) {
+                var followCamera = entity;
+                return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_propertyGrids_cameras_followCameraPropertyGridComponent__WEBPACK_IMPORTED_MODULE_50__["FollowCameraPropertyGridComponent"], { globalState: this.props.globalState, camera: followCamera, lockObject: this._lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }));
+            }
             if (className === "HemisphericLight") {
                 var hemisphericLight = entity;
                 return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_propertyGrids_lights_hemisphericLightPropertyGridComponent__WEBPACK_IMPORTED_MODULE_8__["HemisphericLightPropertyGridComponent"], { globalState: this.props.globalState, light: hemisphericLight, lockObject: this._lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }));
@@ -67311,6 +67317,57 @@ var CommonCameraPropertyGridComponent = /** @class */ (function (_super) {
 
 /***/ }),
 
+/***/ "./components/actionTabs/tabs/propertyGrids/cameras/followCameraPropertyGridComponent.tsx":
+/*!************************************************************************************************!*\
+  !*** ./components/actionTabs/tabs/propertyGrids/cameras/followCameraPropertyGridComponent.tsx ***!
+  \************************************************************************************************/
+/*! exports provided: FollowCameraPropertyGridComponent */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FollowCameraPropertyGridComponent", function() { return FollowCameraPropertyGridComponent; });
+/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ "../../node_modules/react/index.js");
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
+/* harmony import */ var _commonCameraPropertyGridComponent__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./commonCameraPropertyGridComponent */ "./components/actionTabs/tabs/propertyGrids/cameras/commonCameraPropertyGridComponent.tsx");
+/* harmony import */ var _lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../lineContainerComponent */ "./components/actionTabs/lineContainerComponent.tsx");
+/* harmony import */ var _lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../lines/floatLineComponent */ "./components/actionTabs/lines/floatLineComponent.tsx");
+
+
+
+
+
+var FollowCameraPropertyGridComponent = /** @class */ (function (_super) {
+    Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(FollowCameraPropertyGridComponent, _super);
+    function FollowCameraPropertyGridComponent(props) {
+        return _super.call(this, props) || this;
+    }
+    FollowCameraPropertyGridComponent.prototype.render = function () {
+        var camera = this.props.camera;
+        return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "pane" },
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_commonCameraPropertyGridComponent__WEBPACK_IMPORTED_MODULE_2__["CommonCameraPropertyGridComponent"], { globalState: this.props.globalState, lockObject: this.props.lockObject, camera: camera, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "TRANSFORMS" },
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Radius", target: camera, propertyName: "radius", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Rotation offset", target: camera, propertyName: "rotationOffset", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Height offset", target: camera, propertyName: "heightOffset", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Camera acceleration", target: camera, propertyName: "cameraAcceleration", onPropertyChangedObservable: this.props.onPropertyChangedObservable })),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "LIMITS", closed: true },
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Lower radius limit", target: camera, propertyName: "lowerRadiusLimit", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Upper radius limit", target: camera, propertyName: "upperRadiusLimit", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Lower rotation offset limit", target: camera, propertyName: "lowerRotationOffsetLimit", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Upper rotation offset limit", target: camera, propertyName: "upperRotationOffsetLimit", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Lower height offset limit", target: camera, propertyName: "lowerHeightOffsetLimit", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Upper height offset limit", target: camera, propertyName: "upperHeightOffsetLimit", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_floatLineComponent__WEBPACK_IMPORTED_MODULE_4__["FloatLineComponent"], { lockObject: this.props.lockObject, label: "Max camera speed", target: camera, propertyName: "maxCameraSpeed", onPropertyChangedObservable: this.props.onPropertyChangedObservable }))));
+    };
+    return FollowCameraPropertyGridComponent;
+}(react__WEBPACK_IMPORTED_MODULE_1__["Component"]));
+
+
+
+/***/ }),
+
 /***/ "./components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx":
 /*!**********************************************************************************************!*\
   !*** ./components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx ***!
@@ -73559,6 +73616,8 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var babylonjs_Materials_imageProcessingConfiguration__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! babylonjs/Materials/imageProcessingConfiguration */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_imageProcessingConfiguration__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_imageProcessingConfiguration__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var _lines_color3LineComponent__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../../lines/color3LineComponent */ "./components/actionTabs/lines/color3LineComponent.tsx");
+/* harmony import */ var _lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../../lines/buttonLineComponent */ "./components/actionTabs/lines/buttonLineComponent.tsx");
+
 
 
 
@@ -73630,6 +73689,8 @@ var DefaultRenderingPipelinePropertyGridComponent = /** @class */ (function (_su
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Enabled", target: renderPipeline, onValueChanged: function () { return _this.forceUpdate(); }, propertyName: "imageProcessingEnabled", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 renderPipeline.imageProcessing &&
                     react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", null,
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_10__["ButtonLineComponent"], { label: "Convert clear color to linear", onClick: function () { return renderPipeline.scene.clearColor = renderPipeline.scene.clearColor.toLinearSpace(); } }),
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_10__["ButtonLineComponent"], { label: "Convert clear color to gamma", onClick: function () { return renderPipeline.scene.clearColor = renderPipeline.scene.clearColor.toGammaSpace(); } }),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_3__["SliderLineComponent"], { minimum: 0, maximum: 4, step: 0.1, label: "Contrast", target: renderPipeline.imageProcessing, propertyName: "contrast", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_3__["SliderLineComponent"], { minimum: 0, maximum: 4, step: 0.1, label: "Exposure", target: renderPipeline.imageProcessing, propertyName: "exposure", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Tone mapping", target: renderPipeline.imageProcessing, propertyName: "toneMappingEnabled", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
@@ -74515,9 +74576,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ "../../node_modules/react/index.js");
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../lineContainerComponent */ "./components/actionTabs/lineContainerComponent.tsx");
-/* harmony import */ var _lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../lines/buttonLineComponent */ "./components/actionTabs/lines/buttonLineComponent.tsx");
-/* harmony import */ var _lines_optionsLineComponent__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../lines/optionsLineComponent */ "./components/actionTabs/lines/optionsLineComponent.tsx");
-
+/* harmony import */ var _lines_optionsLineComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../lines/optionsLineComponent */ "./components/actionTabs/lines/optionsLineComponent.tsx");
 
 
 
@@ -74547,7 +74606,7 @@ var VariantsPropertyGridComponent = /** @class */ (function (_super) {
         options.splice(0, 0, { label: "Original", value: 0 });
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", null,
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__["LineContainerComponent"], { globalState: this.props.globalState, title: "VARIANTS" },
-                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_optionsLineComponent__WEBPACK_IMPORTED_MODULE_4__["OptionsLineComponent"], { label: "Active variant", options: options, noDirectUpdate: true, target: this.props.host, propertyName: "", onSelect: function (value) {
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_optionsLineComponent__WEBPACK_IMPORTED_MODULE_3__["OptionsLineComponent"], { label: "Active variant", options: options, noDirectUpdate: true, target: this.props.host, propertyName: "", onSelect: function (value) {
                         if (value === 0) {
                             extension.reset(_this.props.host);
                         }
@@ -74565,10 +74624,6 @@ var VariantsPropertyGridComponent = /** @class */ (function (_super) {
                             }
                         }
                         return index;
-                    } }),
-                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_3__["ButtonLineComponent"], { label: "Reset", onClick: function () {
-                        extension.reset(_this.props.host);
-                        _this.forceUpdate();
                     } }))));
     };
     return VariantsPropertyGridComponent;

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 12 - 0
dist/preview release/inspector/babylon.inspector.d.ts

@@ -2725,6 +2725,18 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface IFollowCameraPropertyGridComponentProps {
+        globalState: GlobalState;
+        camera: BABYLON.FollowCamera;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class FollowCameraPropertyGridComponent extends React.Component<IFollowCameraPropertyGridComponentProps> {
+        constructor(props: IFollowCameraPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     export class PropertyGridTabComponent extends PaneComponent {
         private _timerIntervalId;
         private _lockObject;

+ 30 - 0
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -3337,6 +3337,24 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/cameras/followCameraPropertyGridComponent" {
+    import * as React from "react";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
+    import { LockObject } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/lockObject";
+    import { GlobalState } from "babylonjs-inspector/components/globalState";
+    import { FollowCamera } from 'babylonjs/Cameras/followCamera';
+    interface IFollowCameraPropertyGridComponentProps {
+        globalState: GlobalState;
+        camera: FollowCamera;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    }
+    export class FollowCameraPropertyGridComponent extends React.Component<IFollowCameraPropertyGridComponentProps> {
+        constructor(props: IFollowCameraPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGridTabComponent" {
     import { PaneComponent, IPaneComponentProps } from "babylonjs-inspector/components/actionTabs/paneComponent";
     export class PropertyGridTabComponent extends PaneComponent {
@@ -6810,6 +6828,18 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface IFollowCameraPropertyGridComponentProps {
+        globalState: GlobalState;
+        camera: BABYLON.FollowCamera;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class FollowCameraPropertyGridComponent extends React.Component<IFollowCameraPropertyGridComponentProps> {
+        constructor(props: IFollowCameraPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     export class PropertyGridTabComponent extends PaneComponent {
         private _timerIntervalId;
         private _lockObject;

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32",
-        "babylonjs-gui": "4.2.0-alpha.32",
-        "babylonjs-loaders": "4.2.0-alpha.32",
-        "babylonjs-materials": "4.2.0-alpha.32",
-        "babylonjs-serializers": "4.2.0-alpha.32",
-        "babylonjs-gltf2interface": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33",
+        "babylonjs-gui": "4.2.0-alpha.33",
+        "babylonjs-loaders": "4.2.0-alpha.33",
+        "babylonjs-materials": "4.2.0-alpha.33",
+        "babylonjs-serializers": "4.2.0-alpha.33",
+        "babylonjs-gltf2interface": "4.2.0-alpha.33"
     },
     "peerDependencies": {
         "@types/react": ">=16.7.3",

+ 2 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -2880,6 +2880,7 @@ var GLTFFileLoader = /** @class */ (function () {
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         this.onMeshLoadedObservable = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]();
         /**
@@ -2958,6 +2959,7 @@ var GLTFFileLoader = /** @class */ (function () {
     Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set: function (callback) {
             if (this._onMeshLoadedObserver) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylon.glTF1FileLoader.js.map


+ 16 - 5
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -1389,6 +1389,14 @@ var KHR_materials_variants = /** @class */ (function () {
         return ((_b = (_a = rootMesh === null || rootMesh === void 0 ? void 0 : rootMesh.metadata) === null || _a === void 0 ? void 0 : _a.gltf) === null || _b === void 0 ? void 0 : _b[NAME]) || null;
     };
     /** @hidden */
+    KHR_materials_variants.prototype.onLoading = function () {
+        var extensions = this._loader.gltf.extensions;
+        if (extensions && extensions[this.name]) {
+            var extension = extensions[this.name];
+            this._variants = extension.variants;
+        }
+    };
+    /** @hidden */
     KHR_materials_variants.prototype._loadMeshPrimitiveAsync = function (context, name, node, mesh, primitive, assign) {
         var _this = this;
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["GLTFLoader"].LoadExtensionAsync(context, primitive, this.name, function (extensionContext, extension) {
@@ -1407,19 +1415,20 @@ var KHR_materials_variants = /** @class */ (function () {
                     var variants_1 = extensionMetadata.variants;
                     for (var _i = 0, _a = extension.mapping; _i < _a.length; _i++) {
                         var mapping = _a[_i];
-                        var _loop_1 = function (variant) {
+                        var _loop_1 = function (variantIndex) {
+                            var variant = _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["ArrayItem"].Get(extensionContext + "/mapping/" + variantIndex, _this._variants, variantIndex);
                             var material = _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["ArrayItem"].Get("#/materials/", _this._loader.gltf.materials, mapping.material);
                             promises.push(_this._loader._loadMaterialAsync("#/materials/" + mapping.material, material, babylonMesh, babylonDrawMode, function (babylonMaterial) {
-                                variants_1[variant] = variants_1[variant] || [];
-                                variants_1[variant].push({
+                                variants_1[variant.name] = variants_1[variant.name] || [];
+                                variants_1[variant.name].push({
                                     mesh: babylonMesh,
                                     material: babylonMaterial
                                 });
                             }));
                         };
                         for (var _b = 0, _c = mapping.variants; _b < _c.length; _b++) {
-                            var variant = _c[_b];
-                            _loop_1(variant);
+                            var variantIndex = _c[_b];
+                            _loop_1(variantIndex);
                         }
                     }
                 }
@@ -4801,6 +4810,7 @@ var GLTFFileLoader = /** @class */ (function () {
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         this.onMeshLoadedObservable = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]();
         /**
@@ -4879,6 +4889,7 @@ var GLTFFileLoader = /** @class */ (function () {
     Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set: function (callback) {
             if (this._onMeshLoadedObserver) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js.map


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 16 - 5
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -4003,6 +4003,14 @@ var KHR_materials_variants = /** @class */ (function () {
         return ((_b = (_a = rootMesh === null || rootMesh === void 0 ? void 0 : rootMesh.metadata) === null || _a === void 0 ? void 0 : _a.gltf) === null || _b === void 0 ? void 0 : _b[NAME]) || null;
     };
     /** @hidden */
+    KHR_materials_variants.prototype.onLoading = function () {
+        var extensions = this._loader.gltf.extensions;
+        if (extensions && extensions[this.name]) {
+            var extension = extensions[this.name];
+            this._variants = extension.variants;
+        }
+    };
+    /** @hidden */
     KHR_materials_variants.prototype._loadMeshPrimitiveAsync = function (context, name, node, mesh, primitive, assign) {
         var _this = this;
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["GLTFLoader"].LoadExtensionAsync(context, primitive, this.name, function (extensionContext, extension) {
@@ -4021,19 +4029,20 @@ var KHR_materials_variants = /** @class */ (function () {
                     var variants_1 = extensionMetadata.variants;
                     for (var _i = 0, _a = extension.mapping; _i < _a.length; _i++) {
                         var mapping = _a[_i];
-                        var _loop_1 = function (variant) {
+                        var _loop_1 = function (variantIndex) {
+                            var variant = _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["ArrayItem"].Get(extensionContext + "/mapping/" + variantIndex, _this._variants, variantIndex);
                             var material = _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["ArrayItem"].Get("#/materials/", _this._loader.gltf.materials, mapping.material);
                             promises.push(_this._loader._loadMaterialAsync("#/materials/" + mapping.material, material, babylonMesh, babylonDrawMode, function (babylonMaterial) {
-                                variants_1[variant] = variants_1[variant] || [];
-                                variants_1[variant].push({
+                                variants_1[variant.name] = variants_1[variant.name] || [];
+                                variants_1[variant.name].push({
                                     mesh: babylonMesh,
                                     material: babylonMaterial
                                 });
                             }));
                         };
                         for (var _b = 0, _c = mapping.variants; _b < _c.length; _b++) {
-                            var variant = _c[_b];
-                            _loop_1(variant);
+                            var variantIndex = _c[_b];
+                            _loop_1(variantIndex);
                         }
                     }
                 }
@@ -7415,6 +7424,7 @@ var GLTFFileLoader = /** @class */ (function () {
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         this.onMeshLoadedObservable = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]();
         /**
@@ -7493,6 +7503,7 @@ var GLTFFileLoader = /** @class */ (function () {
     Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set: function (callback) {
             if (this._onMeshLoadedObserver) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js.map


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 5 - 0
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -202,11 +202,13 @@ declare module BABYLON {
         preprocessUrlAsync: (url: string) => Promise<string>;
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         private _onMeshLoadedObserver;
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set onMeshLoaded(callback: (mesh: AbstractMesh) => void);
         /**
@@ -1960,6 +1962,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
          */
         enabled: boolean;
         private _loader;
+        private _variants?;
         /** @hidden */
         constructor(loader: GLTFLoader);
         /** @hidden */
@@ -2012,6 +2015,8 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         getLastSelectedVariant(rootMesh: Mesh): Nullable<string | string[]>;
         private static _GetExtensionMetadata;
         /** @hidden */
+        onLoading(): void;
+        /** @hidden */
         _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>>;
     }
 }

+ 16 - 5
dist/preview release/loaders/babylonjs.loaders.js

@@ -5383,6 +5383,14 @@ var KHR_materials_variants = /** @class */ (function () {
         return ((_b = (_a = rootMesh === null || rootMesh === void 0 ? void 0 : rootMesh.metadata) === null || _a === void 0 ? void 0 : _a.gltf) === null || _b === void 0 ? void 0 : _b[NAME]) || null;
     };
     /** @hidden */
+    KHR_materials_variants.prototype.onLoading = function () {
+        var extensions = this._loader.gltf.extensions;
+        if (extensions && extensions[this.name]) {
+            var extension = extensions[this.name];
+            this._variants = extension.variants;
+        }
+    };
+    /** @hidden */
     KHR_materials_variants.prototype._loadMeshPrimitiveAsync = function (context, name, node, mesh, primitive, assign) {
         var _this = this;
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["GLTFLoader"].LoadExtensionAsync(context, primitive, this.name, function (extensionContext, extension) {
@@ -5401,19 +5409,20 @@ var KHR_materials_variants = /** @class */ (function () {
                     var variants_1 = extensionMetadata.variants;
                     for (var _i = 0, _a = extension.mapping; _i < _a.length; _i++) {
                         var mapping = _a[_i];
-                        var _loop_1 = function (variant) {
+                        var _loop_1 = function (variantIndex) {
+                            var variant = _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["ArrayItem"].Get(extensionContext + "/mapping/" + variantIndex, _this._variants, variantIndex);
                             var material = _glTFLoader__WEBPACK_IMPORTED_MODULE_0__["ArrayItem"].Get("#/materials/", _this._loader.gltf.materials, mapping.material);
                             promises.push(_this._loader._loadMaterialAsync("#/materials/" + mapping.material, material, babylonMesh, babylonDrawMode, function (babylonMaterial) {
-                                variants_1[variant] = variants_1[variant] || [];
-                                variants_1[variant].push({
+                                variants_1[variant.name] = variants_1[variant.name] || [];
+                                variants_1[variant.name].push({
                                     mesh: babylonMesh,
                                     material: babylonMaterial
                                 });
                             }));
                         };
                         for (var _b = 0, _c = mapping.variants; _b < _c.length; _b++) {
-                            var variant = _c[_b];
-                            _loop_1(variant);
+                            var variantIndex = _c[_b];
+                            _loop_1(variantIndex);
                         }
                     }
                 }
@@ -8795,6 +8804,7 @@ var GLTFFileLoader = /** @class */ (function () {
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         this.onMeshLoadedObservable = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]();
         /**
@@ -8873,6 +8883,7 @@ var GLTFFileLoader = /** @class */ (function () {
     Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set: function (callback) {
             if (this._onMeshLoadedObserver) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.js.map


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.min.js


+ 10 - 0
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -222,11 +222,13 @@ declare module "babylonjs-loaders/glTF/glTFFileLoader" {
         preprocessUrlAsync: (url: string) => Promise<string>;
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         private _onMeshLoadedObserver;
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set onMeshLoaded(callback: (mesh: AbstractMesh) => void);
         /**
@@ -2115,6 +2117,7 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/KHR_materials_variants" {
          */
         enabled: boolean;
         private _loader;
+        private _variants?;
         /** @hidden */
         constructor(loader: GLTFLoader);
         /** @hidden */
@@ -2167,6 +2170,8 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/KHR_materials_variants" {
         getLastSelectedVariant(rootMesh: Mesh): Nullable<string | string[]>;
         private static _GetExtensionMetadata;
         /** @hidden */
+        onLoading(): void;
+        /** @hidden */
         _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>>;
     }
 }
@@ -3095,11 +3100,13 @@ declare module BABYLON {
         preprocessUrlAsync: (url: string) => Promise<string>;
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         private _onMeshLoadedObserver;
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set onMeshLoaded(callback: (mesh: AbstractMesh) => void);
         /**
@@ -4853,6 +4860,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
          */
         enabled: boolean;
         private _loader;
+        private _variants?;
         /** @hidden */
         constructor(loader: GLTFLoader);
         /** @hidden */
@@ -4905,6 +4913,8 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         getLastSelectedVariant(rootMesh: Mesh): Nullable<string | string[]>;
         private static _GetExtensionMetadata;
         /** @hidden */
+        onLoading(): void;
+        /** @hidden */
         _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>>;
     }
 }

+ 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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.2.0-alpha.32",
-        "babylonjs": "4.2.0-alpha.32"
+        "babylonjs-gltf2interface": "4.2.0-alpha.33",
+        "babylonjs": "4.2.0-alpha.33"
     },
     "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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33"
     },
     "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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33"
     },
     "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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -1 +1 @@
-{"thinEngineOnly":117135,"engineOnly":153575,"sceneOnly":517842,"minGridMaterial":656175,"minStandardMaterial":805600}
+{"thinEngineOnly":117231,"engineOnly":153671,"sceneOnly":517938,"minGridMaterial":656445,"minStandardMaterial":805870}

+ 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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33"
     },
     "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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33"
     },
     "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.2.0-alpha.32",
+    "version": "4.2.0-alpha.33",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.32",
-        "babylonjs-gltf2interface": "4.2.0-alpha.32"
+        "babylonjs": "4.2.0-alpha.33",
+        "babylonjs-gltf2interface": "4.2.0-alpha.33"
     },
     "engines": {
         "node": "*"

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

@@ -19408,6 +19408,10 @@ declare module "babylonjs/Cameras/Inputs/freeCameraTouchInput" {
      */
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse: boolean;
+        /**
          * Defines the camera the input is attached to.
          */
         camera: FreeCamera;
@@ -19428,6 +19432,16 @@ declare module "babylonjs/Cameras/Inputs/freeCameraTouchInput" {
         private _observer;
         private _onLostFocus;
         /**
+         * Manage the touch inputs to control the movement of a free camera.
+         * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+         * @param allowMouse Defines if mouse events can be treated as touch events
+         */
+        constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse?: boolean);
+        /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
          * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -36414,6 +36428,15 @@ declare module "babylonjs/Engines/thinEngine" {
         _removePendingData(data: any): void;
         offlineProvider: IOfflineProvider;
     }
+    /**
+     * Information about the current host
+     */
+    export interface HostInformation {
+        /**
+         * Defines if the current host is a mobile
+         */
+        isMobile: boolean;
+    }
     /** Interface defining initialization parameters for Engine class */
     export interface EngineOptions extends WebGLContextAttributes {
         /**
@@ -36669,6 +36692,10 @@ declare module "babylonjs/Engines/thinEngine" {
         private _activeRequests;
         /** @hidden */
         _transformTextureUrl: Nullable<(url: string) => string>;
+        /**
+         * Gets information about the current host
+         */
+        hostInformation: HostInformation;
         protected get _supportsHardwareTextureRescaling(): boolean;
         private _framebufferDimensionsObject;
         /**
@@ -42793,8 +42820,8 @@ declare module "babylonjs/Audio/sound" {
      */
     export interface ISoundOptions {
         /**
-        * Does the sound autoplay once loaded.
-        */
+         * Does the sound autoplay once loaded.
+         */
         autoplay?: boolean;
         /**
          * Does the sound loop after it finishes playing once.
@@ -42817,9 +42844,9 @@ declare module "babylonjs/Audio/sound" {
          */
         useCustomAttenuation?: boolean;
         /**
-        * Define the roll off factor of spatial sounds.
-        * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-        */
+         * Define the roll off factor of spatial sounds.
+         * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+         */
         rolloffFactor?: number;
         /**
          * Define the reference distance the sound should be heard perfectly.
@@ -42961,13 +42988,13 @@ declare module "babylonjs/Audio/sound" {
         /** @hidden */
         static _SceneComponentInitialization: (scene: Scene) => void;
         /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-        * @param scene defines the scene the sound belongs to
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
+         * Create a sound and attach it to a scene
+         * @param name Name of your sound
+         * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+         * @param scene defines the scene the sound belongs to
+         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+         */
         constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: Nullable<() => void>, options?: ISoundOptions);
         /**
          * Release the sound and its associated resources
@@ -43010,11 +43037,11 @@ declare module "babylonjs/Audio/sound" {
          */
         connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
         /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
+         * Transform this sound into a directional source
+         * @param coneInnerAngle Size of the inner cone in degree
+         * @param coneOuterAngle Size of the outer cone in degree
+         * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+         */
         setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
         /**
          * Gets or sets the inner angle for the directional cone.
@@ -43052,17 +43079,17 @@ declare module "babylonjs/Audio/sound" {
          */
         setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
         /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        * @param offset (optional) Start the sound at a specific time in seconds
-        * @param length (optional) Sound duration (in seconds)
-        */
+         * Play the sound
+         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+         * @param offset (optional) Start the sound at a specific time in seconds
+         * @param length (optional) Sound duration (in seconds)
+         */
         play(time?: number, offset?: number, length?: number): void;
         private _onended;
         /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
+         * Stop the sound
+         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+         */
         stop(time?: number): void;
         /**
          * Put the sound in pause
@@ -48841,31 +48868,35 @@ declare module "babylonjs/XR/webXRFeaturesManager" {
         /**
          * The name of the anchor system feature
          */
-        static ANCHOR_SYSTEM: string;
+        static readonly ANCHOR_SYSTEM: string;
         /**
          * The name of the background remover feature
          */
-        static BACKGROUND_REMOVER: string;
+        static readonly BACKGROUND_REMOVER: string;
         /**
          * The name of the hit test feature
          */
-        static HIT_TEST: string;
+        static readonly HIT_TEST: string;
         /**
          * physics impostors for xr controllers feature
          */
-        static PHYSICS_CONTROLLERS: string;
+        static readonly PHYSICS_CONTROLLERS: string;
         /**
          * The name of the plane detection feature
          */
-        static PLANE_DETECTION: string;
+        static readonly PLANE_DETECTION: string;
         /**
          * The name of the pointer selection feature
          */
-        static POINTER_SELECTION: string;
+        static readonly POINTER_SELECTION: string;
         /**
          * The name of the teleportation feature
          */
-        static TELEPORTATION: string;
+        static readonly TELEPORTATION: string;
+        /**
+         * The name of the feature points feature.
+         */
+        static readonly FEATURE_POINTS: string;
     }
     /**
      * Defining the constructor of a feature. Used to register the modules.
@@ -50643,7 +50674,7 @@ declare module "babylonjs/XR/features/WebXRControllerPointerSelection" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -51191,7 +51222,7 @@ declare module "babylonjs/XR/features/WebXRControllerTeleportation" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -53466,13 +53497,13 @@ declare module "babylonjs/DeviceInput/deviceInputSystem" {
          * @returns Current value of input
          */
         /**
-         * Checks for current device input value, given an id and input index
+         * Checks for current device input value, given an id and input index. Throws exception if requested device not initialized.
          * @param deviceType Enum specifiying device type
          * @param deviceSlot "Slot" or index that device is referenced in
          * @param inputIndex Id of input to be checked
          * @returns Current value of input
          */
-        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): number;
         /**
          * Dispose of all the eventlisteners
          */
@@ -77949,8 +77980,9 @@ declare module "babylonjs/Misc/screenshotTools" {
          * @param antialiasing Whether antialiasing should be turned on or not (default: false)
          * @param fileName A name for for the downloaded file.
          * @param renderSprites Whether the sprites should be rendered or not (default: false)
+         * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
          */
-        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean): void;
+        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean, enableStencilBuffer?: boolean): void;
         /**
          * Generates an image screenshot from the specified camera.
          * @see https://doc.babylonjs.com/how_to/render_scene_on_a_png
@@ -78236,7 +78268,7 @@ declare module "babylonjs/XR/features/WebXRHitTestLegacy" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78383,7 +78415,7 @@ declare module "babylonjs/XR/features/WebXRHitTest" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78491,7 +78523,7 @@ declare module "babylonjs/XR/features/WebXRAnchorSystem" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78627,7 +78659,7 @@ declare module "babylonjs/XR/features/WebXRPlaneDetector" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78717,7 +78749,7 @@ declare module "babylonjs/XR/features/WebXRBackgroundRemover" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78853,7 +78885,7 @@ declare module "babylonjs/XR/features/WebXRControllerPhysics" {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -78920,6 +78952,83 @@ declare module "babylonjs/XR/features/WebXRControllerPhysics" {
         private _detachController;
     }
 }
+declare module "babylonjs/XR/features/WebXRFeaturePointSystem" {
+    import { WebXRSessionManager } from "babylonjs/XR/webXRSessionManager";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { Vector3 } from "babylonjs/Maths/math.vector";
+    import { WebXRAbstractFeature } from "babylonjs/XR/features/WebXRAbstractFeature";
+    /**
+     * A babylon interface for a "WebXR" feature point.
+     * Represents the position and confidence value of a given feature point.
+     */
+    export interface IWebXRFeaturePoint {
+        /**
+         * Represents the position of the feature point in world space.
+         */
+        position: Vector3;
+        /**
+         * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+         */
+        confidenceValue: number;
+    }
+    /**
+     * The feature point system is used to detect feature points from real world geometry.
+     * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+     * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+     */
+    export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+        private _enabled;
+        private _featurePointCloud;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the WebXR specs version
+         */
+        static readonly Version: number;
+        /**
+        * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+        * Will notify the observers about which feature points have been added.
+        */
+        readonly onFeaturePointsAddedObservable: Observable<number[]>;
+        /**
+         * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+         * Will notify the observers about which feature points have been updated.
+         */
+        readonly onFeaturePointsUpdatedObservable: Observable<number[]>;
+        /**
+         * The current feature point cloud maintained across frames.
+         */
+        get featurePointCloud(): Array<IWebXRFeaturePoint>;
+        /**
+         * construct the feature point system
+         * @param _xrSessionManager an instance of xr Session manager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        /**
+         * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+         */
+        protected _onXRFrame(frame: XRFrame): void;
+        /**
+         * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+         */
+        private _init;
+    }
+}
 declare module "babylonjs/XR/features/index" {
     export * from "babylonjs/XR/features/WebXRHitTestLegacy";
     export * from "babylonjs/XR/features/WebXRAnchorSystem";
@@ -78929,6 +79038,7 @@ declare module "babylonjs/XR/features/index" {
     export * from "babylonjs/XR/features/WebXRControllerPointerSelection";
     export * from "babylonjs/XR/features/WebXRControllerPhysics";
     export * from "babylonjs/XR/features/WebXRHitTest";
+    export * from "babylonjs/XR/features/WebXRFeaturePointSystem";
 }
 declare module "babylonjs/XR/motionController/webXRMicrosoftMixedRealityController" {
     import { WebXRAbstractMotionController, IMinimalMotionControllerObject, MotionControllerHandedness } from "babylonjs/XR/motionController/webXRAbstractMotionController";
@@ -98134,6 +98244,10 @@ declare module BABYLON {
      */
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse: boolean;
+        /**
          * Defines the camera the input is attached to.
          */
         camera: FreeCamera;
@@ -98154,6 +98268,16 @@ declare module BABYLON {
         private _observer;
         private _onLostFocus;
         /**
+         * Manage the touch inputs to control the movement of a free camera.
+         * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+         * @param allowMouse Defines if mouse events can be treated as touch events
+         */
+        constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        allowMouse?: boolean);
+        /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
          * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -114427,6 +114551,15 @@ declare module BABYLON {
         _removePendingData(data: any): void;
         offlineProvider: IOfflineProvider;
     }
+    /**
+     * Information about the current host
+     */
+    export interface HostInformation {
+        /**
+         * Defines if the current host is a mobile
+         */
+        isMobile: boolean;
+    }
     /** Interface defining initialization parameters for Engine class */
     export interface EngineOptions extends WebGLContextAttributes {
         /**
@@ -114682,6 +114815,10 @@ declare module BABYLON {
         private _activeRequests;
         /** @hidden */
         _transformTextureUrl: Nullable<(url: string) => string>;
+        /**
+         * Gets information about the current host
+         */
+        hostInformation: HostInformation;
         protected get _supportsHardwareTextureRescaling(): boolean;
         private _framebufferDimensionsObject;
         /**
@@ -120656,8 +120793,8 @@ declare module BABYLON {
      */
     export interface ISoundOptions {
         /**
-        * Does the sound autoplay once loaded.
-        */
+         * Does the sound autoplay once loaded.
+         */
         autoplay?: boolean;
         /**
          * Does the sound loop after it finishes playing once.
@@ -120680,9 +120817,9 @@ declare module BABYLON {
          */
         useCustomAttenuation?: boolean;
         /**
-        * Define the roll off factor of spatial sounds.
-        * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-        */
+         * Define the roll off factor of spatial sounds.
+         * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+         */
         rolloffFactor?: number;
         /**
          * Define the reference distance the sound should be heard perfectly.
@@ -120824,13 +120961,13 @@ declare module BABYLON {
         /** @hidden */
         static _SceneComponentInitialization: (scene: Scene) => void;
         /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-        * @param scene defines the scene the sound belongs to
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
+         * Create a sound and attach it to a scene
+         * @param name Name of your sound
+         * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+         * @param scene defines the scene the sound belongs to
+         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+         */
         constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: Nullable<() => void>, options?: ISoundOptions);
         /**
          * Release the sound and its associated resources
@@ -120873,11 +121010,11 @@ declare module BABYLON {
          */
         connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
         /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
+         * Transform this sound into a directional source
+         * @param coneInnerAngle Size of the inner cone in degree
+         * @param coneOuterAngle Size of the outer cone in degree
+         * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+         */
         setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
         /**
          * Gets or sets the inner angle for the directional cone.
@@ -120915,17 +121052,17 @@ declare module BABYLON {
          */
         setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
         /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        * @param offset (optional) Start the sound at a specific time in seconds
-        * @param length (optional) Sound duration (in seconds)
-        */
+         * Play the sound
+         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+         * @param offset (optional) Start the sound at a specific time in seconds
+         * @param length (optional) Sound duration (in seconds)
+         */
         play(time?: number, offset?: number, length?: number): void;
         private _onended;
         /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
+         * Stop the sound
+         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+         */
         stop(time?: number): void;
         /**
          * Put the sound in pause
@@ -126336,31 +126473,35 @@ declare module BABYLON {
         /**
          * The name of the anchor system feature
          */
-        static ANCHOR_SYSTEM: string;
+        static readonly ANCHOR_SYSTEM: string;
         /**
          * The name of the background remover feature
          */
-        static BACKGROUND_REMOVER: string;
+        static readonly BACKGROUND_REMOVER: string;
         /**
          * The name of the hit test feature
          */
-        static HIT_TEST: string;
+        static readonly HIT_TEST: string;
         /**
          * physics impostors for xr controllers feature
          */
-        static PHYSICS_CONTROLLERS: string;
+        static readonly PHYSICS_CONTROLLERS: string;
         /**
          * The name of the plane detection feature
          */
-        static PLANE_DETECTION: string;
+        static readonly PLANE_DETECTION: string;
         /**
          * The name of the pointer selection feature
          */
-        static POINTER_SELECTION: string;
+        static readonly POINTER_SELECTION: string;
         /**
          * The name of the teleportation feature
          */
-        static TELEPORTATION: string;
+        static readonly TELEPORTATION: string;
+        /**
+         * The name of the feature points feature.
+         */
+        static readonly FEATURE_POINTS: string;
     }
     /**
      * Defining the constructor of a feature. Used to register the modules.
@@ -128073,7 +128214,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -128600,7 +128741,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -130667,13 +130808,13 @@ declare module BABYLON {
          * @returns Current value of input
          */
         /**
-         * Checks for current device input value, given an id and input index
+         * Checks for current device input value, given an id and input index. Throws exception if requested device not initialized.
          * @param deviceType Enum specifiying device type
          * @param deviceSlot "Slot" or index that device is referenced in
          * @param inputIndex Id of input to be checked
          * @returns Current value of input
          */
-        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): number;
         /**
          * Dispose of all the eventlisteners
          */
@@ -153142,8 +153283,9 @@ declare module BABYLON {
          * @param antialiasing Whether antialiasing should be turned on or not (default: false)
          * @param fileName A name for for the downloaded file.
          * @param renderSprites Whether the sprites should be rendered or not (default: false)
+         * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
          */
-        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean): void;
+        static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType?: string, samples?: number, antialiasing?: boolean, fileName?: string, renderSprites?: boolean, enableStencilBuffer?: boolean): void;
         /**
          * Generates an image screenshot from the specified camera.
          * @see https://doc.babylonjs.com/how_to/render_scene_on_a_png
@@ -153369,7 +153511,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153511,7 +153653,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153613,7 +153755,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153744,7 +153886,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153830,7 +153972,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -153960,7 +154102,7 @@ declare module BABYLON {
         /**
          * The module's name
          */
-        static readonly Name: string;
+        static readonly Name: string;
         /**
          * The (Babylon) version of this module.
          * This is an integer representing the implementation version.
@@ -154029,6 +154171,79 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * A babylon interface for a "WebXR" feature point.
+     * Represents the position and confidence value of a given feature point.
+     */
+    export interface IWebXRFeaturePoint {
+        /**
+         * Represents the position of the feature point in world space.
+         */
+        position: Vector3;
+        /**
+         * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+         */
+        confidenceValue: number;
+    }
+    /**
+     * The feature point system is used to detect feature points from real world geometry.
+     * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+     * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+     */
+    export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+        private _enabled;
+        private _featurePointCloud;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the WebXR specs version
+         */
+        static readonly Version: number;
+        /**
+        * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+        * Will notify the observers about which feature points have been added.
+        */
+        readonly onFeaturePointsAddedObservable: Observable<number[]>;
+        /**
+         * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+         * Will notify the observers about which feature points have been updated.
+         */
+        readonly onFeaturePointsUpdatedObservable: Observable<number[]>;
+        /**
+         * The current feature point cloud maintained across frames.
+         */
+        get featurePointCloud(): Array<IWebXRFeaturePoint>;
+        /**
+         * construct the feature point system
+         * @param _xrSessionManager an instance of xr Session manager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        /**
+         * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+         */
+        protected _onXRFrame(frame: XRFrame): void;
+        /**
+         * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+         */
+        private _init;
+    }
+}
+declare module BABYLON {
+    /**
      * The motion controller class for all microsoft mixed reality controllers
      */
     export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionController {
@@ -154993,4 +155208,15 @@ interface XRPlane {
     planeSpace: XRSpace;
     polygon: Array<DOMPointReadOnly>;
     lastChangedTime: number;
+}
+// This file contains native only extensions for WebXR  These APIs are not supported in the browser yet.
+// They are intended for use with either Babylon Native https://github.com/BabylonJS/BabylonNative or
+// Babylon React Native: https://github.com/BabylonJS/BabylonReactNative
+
+interface XRSession {
+    trySetFeaturePointCloudEnabled(enabled: boolean): boolean;
+}
+
+interface XRFrame {
+    featurePointCloud? : Array<number>;
 }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 24 - 20
dist/preview release/viewer/babylon.viewer.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


+ 10 - 0
dist/preview release/viewer/babylonjs.loaders.module.d.ts

@@ -222,11 +222,13 @@ declare module "babylonjs-loaders/glTF/glTFFileLoader" {
         preprocessUrlAsync: (url: string) => Promise<string>;
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         private _onMeshLoadedObserver;
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set onMeshLoaded(callback: (mesh: AbstractMesh) => void);
         /**
@@ -2115,6 +2117,7 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/KHR_materials_variants" {
          */
         enabled: boolean;
         private _loader;
+        private _variants?;
         /** @hidden */
         constructor(loader: GLTFLoader);
         /** @hidden */
@@ -2167,6 +2170,8 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/KHR_materials_variants" {
         getLastSelectedVariant(rootMesh: Mesh): Nullable<string | string[]>;
         private static _GetExtensionMetadata;
         /** @hidden */
+        onLoading(): void;
+        /** @hidden */
         _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>>;
     }
 }
@@ -3095,11 +3100,13 @@ declare module BABYLON {
         preprocessUrlAsync: (url: string) => Promise<string>;
         /**
          * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         private _onMeshLoadedObserver;
         /**
          * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
          */
         set onMeshLoaded(callback: (mesh: AbstractMesh) => void);
         /**
@@ -4853,6 +4860,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
          */
         enabled: boolean;
         private _loader;
+        private _variants?;
         /** @hidden */
         constructor(loader: GLTFLoader);
         /** @hidden */
@@ -4905,6 +4913,8 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         getLastSelectedVariant(rootMesh: Mesh): Nullable<string | string[]>;
         private static _GetExtensionMetadata;
         /** @hidden */
+        onLoading(): void;
+        /** @hidden */
         _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>>;
     }
 }

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

@@ -9,7 +9,7 @@
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added SubSurfaceScattering on PBR materials ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
-- Added Curve editor to manage selected entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added Curve editor to manage entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added support for **thin instances** for faster mesh instances. [Doc](https://doc.babylonjs.com/how_to/how_to_use_thininstances) ([Popov72](https://github.com/Popov72))
 
@@ -73,6 +73,7 @@
 
 ### Inspector
 
+- Added support for follow cameras ([Deltakosh](https://github.com/deltakosh))
 - New delta tool used to apply delta changes to a scene ([Deltakosh](https://github.com/deltakosh))
 - Handle PBR colors as colors in linear space ([Popov72](https://github.com/Popov72))
 - Allow removing textures ([Popov72](https://github.com/Popov72))
@@ -169,7 +170,11 @@
 - All camera view matrices are now calculated by Babylon to support left and right handed systems ([RaananW](https://github.com/RaananW))
 - WebXR Features Manager now has the ability to check if a feature can be enabled, and set native features optional or required ([RaananW](https://github.com/RaananW))
 - Optional camera gaze mode added to the pointer selection feature ([RaananW](https://github.com/RaananW))
+- Exposing feature points when running on top of BabylonNative ([Alex-MSFT](https://github.com/Alex-MSFT))
 - WebXR hit test can now define different entity type for the results ([#8687](https://github.com/BabylonJS/Babylon.js/issues/8687)) ([RaananW](https://github.com/RaananW))
+- Fixed an issue with stencil not enabled per default ([#8720](https://github.com/BabylonJS/Babylon.js/issues/8720)) ([RaananW](https://github.com/RaananW))
+- Expose the overlay to which the XR Enter/Exit buttons are added to ([#8754](https://github.com/BabylonJS/Babylon.js/issues/8754)) ([RaananW](https://github.com/RaananW))
+- WebXR hand-tracking module is available, able to track hand-joints on selected devices including physics interactions ([RaananW](https://github.com/RaananW))
 
 ### Collisions
 
@@ -282,6 +287,8 @@
 - Fix docs Matrix.RotationYawPitchRoll and Matrix.RotationYawPitchRollToRef ([VSerain](https://github.com/VSerain))
 - Fix issue in `GLTFLoader._updateBoneMatrices()` where bone rest position was not set. ([drigax](https://github.com/drigax))
 - Fix the bounding box of instances that does not match the instance position / rotation / scaling ([Popov72](https://github.com/Popov72))
+- Fix an issue with sound updateOptions not updating the underlying sound buffer/html element ([RaananW](https://github.com/RaananW))
+- Fixed bug in sphereBuilder where top and bottom segments added 6 indices per triangle instead of 3. ([aWeirdo](https://github.com/aWeirdo))
 
 ## Breaking changes
 - `FollowCamera.target` was renamed to `FollowCamera.meshTarget` to not be in conflict with `TargetCamera.target` ([Deltakosh](https://github.com/deltakosh))

+ 9 - 0
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -96,6 +96,8 @@ import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
 import { SpritePropertyGridComponent } from './propertyGrids/sprites/spritePropertyGridComponent';
 import { Sprite } from 'babylonjs/Sprites/sprite';
 import { TargetedAnimationGridComponent } from './propertyGrids/animations/targetedAnimationPropertyGridComponent';
+import { FollowCamera } from 'babylonjs/Cameras/followCamera';
+import { FollowCameraPropertyGridComponent } from './propertyGrids/cameras/followCameraPropertyGridComponent';
 
 export class PropertyGridTabComponent extends PaneComponent {
     private _timerIntervalId: number;
@@ -197,6 +199,13 @@ export class PropertyGridTabComponent extends PaneComponent {
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className.indexOf("FollowCamera") !== -1) {
+                const followCamera = entity as FollowCamera;
+                return (<FollowCameraPropertyGridComponent globalState={this.props.globalState} camera={followCamera}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
             if (className === "HemisphericLight") {
                 const hemisphericLight = entity as HemisphericLight;
                 return (<HemisphericLightPropertyGridComponent

+ 49 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/followCameraPropertyGridComponent.tsx

@@ -0,0 +1,49 @@
+import * as React from "react";
+
+import { Observable } from "babylonjs/Misc/observable";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonCameraPropertyGridComponent } from "./commonCameraPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { LockObject } from "../lockObject";
+import { GlobalState } from '../../../../globalState';
+import { FollowCamera } from 'babylonjs/Cameras/followCamera';
+
+interface IFollowCameraPropertyGridComponentProps {
+    globalState: GlobalState;
+    camera: FollowCamera;
+    lockObject: LockObject;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+}
+
+export class FollowCameraPropertyGridComponent extends React.Component<IFollowCameraPropertyGridComponentProps> {
+    constructor(props: IFollowCameraPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const camera = this.props.camera;
+
+        return (
+            <div className="pane">
+                <CommonCameraPropertyGridComponent globalState={this.props.globalState} lockObject={this.props.lockObject} camera={camera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent globalState={this.props.globalState} title="TRANSFORMS">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Radius" target={camera} propertyName="radius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Rotation offset" target={camera} propertyName="rotationOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Height offset" target={camera} propertyName="heightOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Camera acceleration" target={camera} propertyName="cameraAcceleration" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="LIMITS" closed={true}>
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Lower radius limit" target={camera} propertyName="lowerRadiusLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Upper radius limit" target={camera} propertyName="upperRadiusLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Lower rotation offset limit" target={camera} propertyName="lowerRotationOffsetLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Upper rotation offset limit" target={camera} propertyName="upperRotationOffsetLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Lower height offset limit" target={camera} propertyName="lowerHeightOffsetLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Upper height offset limit" target={camera} propertyName="upperHeightOffsetLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max camera speed" target={camera} propertyName="maxCameraSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 4 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/postProcesses/defaultRenderingPipelinePropertyGridComponent.tsx

@@ -14,6 +14,7 @@ import { OptionsLineComponent } from '../../../lines/optionsLineComponent';
 import { ImageProcessingConfiguration } from 'babylonjs/Materials/imageProcessingConfiguration';
 import { Color3LineComponent } from '../../../lines/color3LineComponent';
 import { GlobalState } from '../../../../globalState';
+import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
 
 interface IDefaultRenderingPipelinePropertyGridComponentProps {
     globalState: GlobalState;
@@ -118,7 +119,9 @@ export class DefaultRenderingPipelinePropertyGridComponent extends React.Compone
                         propertyName="imageProcessingEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
                         renderPipeline.imageProcessing &&
-                        <div>
+                        <div>                                                        
+                            <ButtonLineComponent label="Convert clear color to linear" onClick={() => renderPipeline.scene.clearColor = renderPipeline.scene.clearColor.toLinearSpace()} />
+                            <ButtonLineComponent label="Convert clear color to gamma" onClick={() => renderPipeline.scene.clearColor = renderPipeline.scene.clearColor.toGammaSpace()} />
                             <SliderLineComponent minimum={0} maximum={4} step={0.1} label="Contrast" target={renderPipeline.imageProcessing} propertyName="contrast" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             <SliderLineComponent minimum={0} maximum={4} step={0.1} label="Exposure" target={renderPipeline.imageProcessing} propertyName="exposure" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             <CheckBoxLineComponent label="Tone mapping" target={renderPipeline.imageProcessing} propertyName="toneMappingEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 0 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/variantsPropertyGridComponent.tsx

@@ -7,7 +7,6 @@ import { PropertyChangedEvent } from "../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../lineContainerComponent";
 import { LockObject } from "./lockObject";
 import { GlobalState } from "../../../globalState";
-import { ButtonLineComponent } from '../../lines/buttonLineComponent';
 import { OptionsLineComponent } from '../../lines/optionsLineComponent';
 
 declare type KHR_materials_variants = import("babylonjs-loaders/glTF/2.0/Extensions/KHR_materials_variants").KHR_materials_variants;
@@ -74,10 +73,6 @@ export class VariantsPropertyGridComponent extends React.Component<IVariantsProp
                         return index;
                     }}
                 />
-                    <ButtonLineComponent label="Reset" onClick={() => {
-                        extension.reset(this.props.host);
-                        this.forceUpdate();
-                    }} />
                 </LineContainerComponent>
             </div>
         );

+ 17 - 5
loaders/src/glTF/2.0/Extensions/KHR_materials_variants.ts

@@ -6,7 +6,7 @@ import { Material } from 'babylonjs/Materials/material';
 import { Mesh } from 'babylonjs/Meshes/mesh';
 import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
 import { INode, IMeshPrimitive, IMesh } from '../glTFLoaderInterfaces';
-import { IKHRMaterialVariants } from 'babylonjs-gltf2interface';
+import { IKHRMaterialVariants_Mapping, IKHRMaterialVariants_Variant, IKHRMaterialVariants_Variants } from 'babylonjs-gltf2interface';
 
 const NAME = "KHR_materials_variants";
 
@@ -37,6 +37,8 @@ export class KHR_materials_variants implements IGLTFLoaderExtension {
 
     private _loader: GLTFLoader;
 
+    private _variants?: Array<IKHRMaterialVariants_Variant>;
+
     /** @hidden */
     constructor(loader: GLTFLoader) {
         this._loader = loader;
@@ -164,8 +166,17 @@ export class KHR_materials_variants implements IGLTFLoaderExtension {
     }
 
     /** @hidden */
+    public onLoading(): void {
+        const extensions = this._loader.gltf.extensions;
+        if (extensions && extensions[this.name]) {
+            const extension = extensions[this.name] as IKHRMaterialVariants_Variants;
+            this._variants = extension.variants;
+        }
+    }
+
+    /** @hidden */
     public _loadMeshPrimitiveAsync(context: string, name: string, node: INode, mesh: IMesh, primitive: IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Nullable<Promise<AbstractMesh>> {
-        return GLTFLoader.LoadExtensionAsync<IKHRMaterialVariants, AbstractMesh>(context, primitive, this.name, (extensionContext, extension) => {
+        return GLTFLoader.LoadExtensionAsync<IKHRMaterialVariants_Mapping, AbstractMesh>(context, primitive, this.name, (extensionContext, extension) => {
             const promises = new Array<Promise<any>>();
             promises.push(this._loader._loadMeshPrimitiveAsync(context, name, node, mesh, primitive, (babylonMesh) => {
                 assign(babylonMesh);
@@ -184,11 +195,12 @@ export class KHR_materials_variants implements IGLTFLoaderExtension {
                     // For each mapping, look at the variants and make a new entry for them.
                     const variants = extensionMetadata.variants;
                     for (const mapping of extension.mapping) {
-                        for (const variant of mapping.variants) {
+                        for (const variantIndex of mapping.variants) {
+                            const variant = ArrayItem.Get(`${extensionContext}/mapping/${variantIndex}`, this._variants, variantIndex);
                             const material = ArrayItem.Get(`#/materials/`, this._loader.gltf.materials, mapping.material);
                             promises.push(this._loader._loadMaterialAsync(`#/materials/${mapping.material}`, material, babylonMesh, babylonDrawMode, (babylonMaterial) => {
-                                variants[variant] = variants[variant] || [];
-                                variants[variant].push({
+                                variants[variant.name] = variants[variant.name] || [];
+                                variants[variant.name].push({
                                     mesh: babylonMesh,
                                     material: babylonMaterial
                                 });

+ 2 - 0
loaders/src/glTF/glTFFileLoader.ts

@@ -251,6 +251,7 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
 
     /**
      * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+     * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
      */
     public readonly onMeshLoadedObservable = new Observable<AbstractMesh>();
 
@@ -258,6 +259,7 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
 
     /**
      * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+     * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
      */
     public set onMeshLoaded(callback: (mesh: AbstractMesh) => void) {
         if (this._onMeshLoadedObserver) {

+ 1 - 1
package.json

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

+ 18 - 0
src/Animations/animation.ts

@@ -1029,6 +1029,15 @@ export class Animation {
             switch (dataType) {
                 case Animation.ANIMATIONTYPE_FLOAT:
                     key.values = [animationKey.value];
+                    if (animationKey.inTangent !== undefined) {
+                        key.values.push(animationKey.inTangent);
+                    }
+                    if (animationKey.outTangent !== undefined) {
+                        if (animationKey.inTangent === undefined) {
+                            key.values.push(undefined);
+                        }
+                        key.values.push(animationKey.outTangent);
+                    }
                     break;
                 case Animation.ANIMATIONTYPE_QUATERNION:
                 case Animation.ANIMATIONTYPE_MATRIX:
@@ -1036,6 +1045,15 @@ export class Animation {
                 case Animation.ANIMATIONTYPE_COLOR3:
                 case Animation.ANIMATIONTYPE_COLOR4:
                     key.values = animationKey.value.asArray();
+                    if (animationKey.inTangent != undefined) {
+                        key.values.push(animationKey.inTangent.asArray());
+                    }
+                    if (animationKey.outTangent != undefined) {
+                        if (animationKey.inTangent === undefined) {
+                            key.values.push(undefined);
+                        }
+                        key.values.push(animationKey.outTangent.asArray());
+                    }
                     break;
             }
 

+ 150 - 108
src/Audio/sound.ts

@@ -7,15 +7,15 @@ import { Engine } from "../Engines/engine";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { TransformNode } from "../Meshes/transformNode";
 import { Logger } from "../Misc/logger";
-import { _DevTools } from '../Misc/devTools';
+import { _DevTools } from "../Misc/devTools";
 
 /**
  * Interface used to define options for Sound class
  */
 export interface ISoundOptions {
     /**
-    * Does the sound autoplay once loaded.
-    */
+     * Does the sound autoplay once loaded.
+     */
     autoplay?: boolean;
     /**
      * Does the sound loop after it finishes playing once.
@@ -38,9 +38,9 @@ export interface ISoundOptions {
      */
     useCustomAttenuation?: boolean;
     /**
-    * Define the roll off factor of spatial sounds.
-    * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-    */
+     * Define the roll off factor of spatial sounds.
+     * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+     */
     rolloffFactor?: number;
     /**
      * Define the reference distance the sound should be heard perfectly.
@@ -181,23 +181,23 @@ export class Sound {
     private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
     private _isOutputConnected = false;
     private _htmlAudioElement: HTMLAudioElement;
-    private _urlType: 'Unknown' | 'String' | 'Array' | 'ArrayBuffer' | 'MediaStream' = "Unknown";
+    private _urlType: "Unknown" | "String" | "Array" | "ArrayBuffer" | "MediaStream" = "Unknown";
     private _length?: number;
     private _offset?: number;
 
     /** @hidden */
     public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
         throw _DevTools.WarnImport("AudioSceneComponent");
-    }
+    };
 
     /**
-    * Create a sound and attach it to a scene
-    * @param name Name of your sound
-    * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-    * @param scene defines the scene the sound belongs to
-    * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-    * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-    */
+     * Create a sound and attach it to a scene
+     * @param name Name of your sound
+     * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+     * @param scene defines the scene the sound belongs to
+     * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+     * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+     */
     constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback: Nullable<() => void> = null, options?: ISoundOptions) {
         this.name = name;
         this._scene = scene;
@@ -208,8 +208,7 @@ export class Sound {
         this._customAttenuationFunction = (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => {
             if (currentDistance < maxDistance) {
                 return currentVolume * (1 - currentDistance / maxDistance);
-            }
-            else {
+            } else {
                 return 0;
             }
         };
@@ -246,7 +245,7 @@ export class Sound {
             // if no parameter is passed, you need to call setAudioBuffer yourself to prepare the sound
             if (urlOrArrayBuffer) {
                 try {
-                    if (typeof (urlOrArrayBuffer) === "string") {
+                    if (typeof urlOrArrayBuffer === "string") {
                         this._urlType = "String";
                     } else if (urlOrArrayBuffer instanceof ArrayBuffer) {
                         this._urlType = "ArrayBuffer";
@@ -282,27 +281,37 @@ export class Sound {
                         case "String":
                             urls.push(urlOrArrayBuffer);
                         case "Array":
-                            if (urls.length === 0) { urls = urlOrArrayBuffer; }
+                            if (urls.length === 0) {
+                                urls = urlOrArrayBuffer;
+                            }
                             // If we found a supported format, we load it immediately and stop the loop
                             for (var i = 0; i < urls.length; i++) {
                                 var url = urls[i];
-                                codecSupportedFound = (options && options.skipCodecCheck) ||
+                                codecSupportedFound =
+                                    (options && options.skipCodecCheck) ||
                                     (url.indexOf(".mp3", url.length - 4) !== -1 && Engine.audioEngine.isMP3supported) ||
                                     (url.indexOf(".ogg", url.length - 4) !== -1 && Engine.audioEngine.isOGGsupported) ||
-                                    (url.indexOf(".wav", url.length - 4) !== -1) ||
-                                    (url.indexOf("blob:") !== -1);
+                                    url.indexOf(".wav", url.length - 4) !== -1 ||
+                                    url.indexOf("blob:") !== -1;
                                 if (codecSupportedFound) {
                                     // Loading sound using XHR2
                                     if (!this._streaming) {
-                                        this._scene._loadFile(url, (data) => {
-                                            this._soundLoaded(data as ArrayBuffer);
-                                        }, undefined, true, true, (exception) => {
-                                            if (exception) {
-                                                Logger.Error("XHR " + exception.status + " error on: " + url + ".");
+                                        this._scene._loadFile(
+                                            url,
+                                            (data) => {
+                                                this._soundLoaded(data as ArrayBuffer);
+                                            },
+                                            undefined,
+                                            true,
+                                            true,
+                                            (exception) => {
+                                                if (exception) {
+                                                    Logger.Error("XHR " + exception.status + " error on: " + url + ".");
+                                                }
+                                                Logger.Error("Sound creation aborted.");
+                                                this._scene.mainSoundTrack.RemoveSound(this);
                                             }
-                                            Logger.Error("Sound creation aborted.");
-                                            this._scene.mainSoundTrack.RemoveSound(this);
-                                        });
+                                        );
                                     }
                                     // Streaming sound using HTML5 Audio tag
                                     else {
@@ -334,8 +343,7 @@ export class Sound {
 
                     if (!validParameter) {
                         Logger.Error("Parameter must be a URL to the sound, an Array of URLs (.mp3 & .ogg) or an ArrayBuffer of the sound.");
-                    }
-                    else {
+                    } else {
                         if (!codecSupportedFound) {
                             this._isReadyToPlay = true;
                             // Simulating a ready to play event to avoid breaking code path
@@ -348,14 +356,12 @@ export class Sound {
                             }
                         }
                     }
-                }
-                catch (ex) {
+                } catch (ex) {
                     Logger.Error("Unexpected error. Sound creation aborted.");
                     this._scene.mainSoundTrack.RemoveSound(this);
                 }
             }
-        }
-        else {
+        } else {
             // Adding an empty sound to avoid breaking audio calls for non Web Audio browsers
             this._scene.mainSoundTrack.AddSound(this);
             if (!Engine.audioEngine.WarnedWebAudioUnsupported) {
@@ -384,8 +390,7 @@ export class Sound {
             this._isReadyToPlay = false;
             if (this.soundTrackId === -1) {
                 this._scene.mainSoundTrack.RemoveSound(this);
-            }
-            else if (this._scene.soundTracks) {
+            } else if (this._scene.soundTracks) {
                 this._scene.soundTracks[this.soundTrackId].RemoveSound(this);
             }
             if (this._soundGain) {
@@ -431,12 +436,22 @@ export class Sound {
         if (!Engine.audioEngine.audioContext) {
             return;
         }
-        Engine.audioEngine.audioContext.decodeAudioData(audioData, (buffer) => {
-            this._audioBuffer = buffer;
-            this._isReadyToPlay = true;
-            if (this.autoplay) { this.play(0, this._offset, this._length); }
-            if (this._readyToPlayCallback) { this._readyToPlayCallback(); }
-        }, (err: any) => { Logger.Error("Error while decoding audio data for: " + this.name + " / Error: " + err); });
+        Engine.audioEngine.audioContext.decodeAudioData(
+            audioData,
+            (buffer) => {
+                this._audioBuffer = buffer;
+                this._isReadyToPlay = true;
+                if (this.autoplay) {
+                    this.play(0, this._offset, this._length);
+                }
+                if (this._readyToPlayCallback) {
+                    this._readyToPlayCallback();
+                }
+            },
+            (err: any) => {
+                Logger.Error("Error while decoding audio data for: " + this.name + " / Error: " + err);
+            }
+        );
     }
 
     /**
@@ -469,10 +484,21 @@ export class Sound {
             if (this.isPlaying) {
                 if (this._streaming && this._htmlAudioElement) {
                     this._htmlAudioElement.playbackRate = this._playbackRate;
-                }
-                else {
+                    if (this._htmlAudioElement.loop !== this.loop) {
+                        this._htmlAudioElement.loop = this.loop;
+                    }
+                } else {
                     if (this._soundSource) {
                         this._soundSource.playbackRate.value = this._playbackRate;
+                        if (this._soundSource.loop !== this.loop) {
+                            this._soundSource.loop = this.loop;
+                        }
+                        if (this._offset !== undefined && this._soundSource.loopStart !== this._offset) {
+                            this._soundSource.loopStart = this._offset;
+                        }
+                        if (this._length !== undefined && this._length !== this._soundSource.loopEnd) {
+                            this._soundSource.loopEnd = (this._offset! | 0) + this._length!;
+                        }
                     }
                 }
             }
@@ -500,8 +526,7 @@ export class Sound {
                 this._soundPanner.refDistance = 1;
                 this._soundPanner.rolloffFactor = 1;
                 this._soundPanner.panningModel = this._panningModel as any;
-            }
-            else {
+            } else {
                 this._soundPanner.distanceModel = this.distanceModel as any;
                 this._soundPanner.maxDistance = this.maxDistance;
                 this._soundPanner.refDistance = this.refDistance;
@@ -552,11 +577,11 @@ export class Sound {
     }
 
     /**
-    * Transform this sound into a directional source
-    * @param coneInnerAngle Size of the inner cone in degree
-    * @param coneOuterAngle Size of the outer cone in degree
-    * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-    */
+     * Transform this sound into a directional source
+     * @param coneInnerAngle Size of the inner cone in degree
+     * @param coneOuterAngle Size of the outer cone in degree
+     * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+     */
     public setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void {
         if (coneOuterAngle < coneInnerAngle) {
             Logger.Error("setDirectionalCone(): outer angle of the cone must be superior or equal to the inner angle.");
@@ -674,11 +699,11 @@ export class Sound {
     }
 
     /**
-    * Play the sound
-    * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-    * @param offset (optional) Start the sound at a specific time in seconds
-    * @param length (optional) Sound duration (in seconds)
-    */
+     * Play the sound
+     * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+     * @param offset (optional) Start the sound at a specific time in seconds
+     * @param length (optional) Sound duration (in seconds)
+     */
     public play(time?: number, offset?: number, length?: number): void {
         if (this._isReadyToPlay && this._scene.audioEnabled && Engine.audioEngine.audioContext) {
             try {
@@ -698,8 +723,7 @@ export class Sound {
                             this._soundPanner.coneOuterGain = this._coneOuterGain;
                             if (this._connectedTransformNode) {
                                 this._updateDirection();
-                            }
-                            else {
+                            } else {
                                 this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
                             }
                         }
@@ -708,7 +732,9 @@ export class Sound {
                 if (this._streaming) {
                     if (!this._streamingSource) {
                         this._streamingSource = Engine.audioEngine.audioContext.createMediaElementSource(this._htmlAudioElement);
-                        this._htmlAudioElement.onended = () => { this._onended(); };
+                        this._htmlAudioElement.onended = () => {
+                            this._onended();
+                        };
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                     }
                     this._streamingSource.disconnect();
@@ -730,21 +756,23 @@ export class Sound {
                                         // Waiting for the audio engine to be unlocked by user click on unmute
                                         Engine.audioEngine.lock();
                                         if (this.loop || this.autoplay) {
-                                            Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                            Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
+                                                tryToPlay();
+                                            });
                                         }
                                     });
                                 }
-                            }
-                            else {
+                            } else {
                                 if (this.loop || this.autoplay) {
-                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
+                                        tryToPlay();
+                                    });
                                 }
                             }
                         };
                         tryToPlay();
                     }
-                }
-                else {
+                } else {
                     var tryToPlay = () => {
                         if (Engine.audioEngine.audioContext) {
                             length = length || this._length;
@@ -767,7 +795,9 @@ export class Sound {
                                 this._soundSource.loopEnd = (offset! | 0) + length!;
                             }
                             this._soundSource.playbackRate.value = this._playbackRate;
-                            this._soundSource.onended = () => { this._onended(); };
+                            this._soundSource.onended = () => {
+                                this._onended();
+                            };
                             startTime = time ? Engine.audioEngine.audioContext!.currentTime + time : Engine.audioEngine.audioContext!.currentTime;
                             const actualOffset = this.isPaused ? this._startOffset % this._soundSource!.buffer!.duration : offset ? offset : 0;
                             this._soundSource!.start(startTime, actualOffset, this.loop ? undefined : length);
@@ -782,23 +812,22 @@ export class Sound {
                                 // Waiting for the audio engine to be unlocked by user click on unmute
                                 Engine.audioEngine.lock();
                                 if (this.loop || this.autoplay) {
-                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
+                                        tryToPlay();
+                                    });
                                 }
-                            }
-                            else {
+                            } else {
                                 tryToPlay();
                             }
                         }, 500);
-                    }
-                    else {
+                    } else {
                         tryToPlay();
                     }
                 }
                 this._startTime = startTime;
                 this.isPlaying = true;
                 this.isPaused = false;
-            }
-            catch (ex) {
+            } catch (ex) {
                 Logger.Error("Error while trying to play audio: " + this.name + ", " + ex.message);
             }
         }
@@ -813,9 +842,9 @@ export class Sound {
     }
 
     /**
-    * Stop the sound
-    * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-    */
+     * Stop the sound
+     * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+     */
     public stop(time?: number): void {
         if (this.isPlaying) {
             if (this._streaming) {
@@ -829,11 +858,12 @@ export class Sound {
                     this._streamingSource.disconnect();
                 }
                 this.isPlaying = false;
-            }
-            else if (Engine.audioEngine.audioContext && this._soundSource) {
+            } else if (Engine.audioEngine.audioContext && this._soundSource) {
                 var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                 this._soundSource.stop(stopTime);
-                this._soundSource.onended = () => { this.isPlaying = false; };
+                this._soundSource.onended = () => {
+                    this.isPlaying = false;
+                };
                 if (!this.isPaused) {
                     this._startOffset = 0;
                 }
@@ -853,8 +883,7 @@ export class Sound {
                 } else {
                     this._streamingSource.disconnect();
                 }
-            }
-            else if (Engine.audioEngine.audioContext) {
+            } else if (Engine.audioEngine.audioContext) {
                 this.stop(0);
                 this._startOffset += Engine.audioEngine.audioContext.currentTime - this._startTime;
             }
@@ -872,8 +901,7 @@ export class Sound {
                 this._soundGain.gain.cancelScheduledValues(Engine.audioEngine.audioContext.currentTime);
                 this._soundGain.gain.setValueAtTime(this._soundGain.gain.value, Engine.audioEngine.audioContext.currentTime);
                 this._soundGain.gain.linearRampToValueAtTime(newVolume, Engine.audioEngine.audioContext.currentTime + time);
-            }
-            else {
+            } else {
                 this._soundGain.gain.value = newVolume;
             }
         }
@@ -889,8 +917,7 @@ export class Sound {
         if (this.isPlaying) {
             if (this._streaming && this._htmlAudioElement) {
                 this._htmlAudioElement.playbackRate = this._playbackRate;
-            }
-            else if (this._soundSource) {
+            } else if (this._soundSource) {
                 this._soundSource.playbackRate.value = this._playbackRate;
             }
         }
@@ -944,8 +971,7 @@ export class Sound {
         if (this._positionInEmitterSpace) {
             node.worldMatrixFromCache.invertToRef(TmpVectors.Matrix[0]);
             this.setPosition(TmpVectors.Matrix[0].getTranslation());
-        }
-        else {
+        } else {
             if (!(<any>node).getBoundingInfo) {
                 this.setPosition(node.absolutePosition);
             } else {
@@ -969,18 +995,24 @@ export class Sound {
                 if (this._isReadyToPlay) {
                     clonedSound._audioBuffer = this.getAudioBuffer();
                     clonedSound._isReadyToPlay = true;
-                    if (clonedSound.autoplay) { clonedSound.play(0, this._offset, this._length); }
-                }
-                else {
+                    if (clonedSound.autoplay) {
+                        clonedSound.play(0, this._offset, this._length);
+                    }
+                } else {
                     window.setTimeout(setBufferAndRun, 300);
                 }
             };
 
             var currentOptions = {
-                autoplay: this.autoplay, loop: this.loop,
-                volume: this._volume, spatialSound: this.spatialSound, maxDistance: this.maxDistance,
-                useCustomAttenuation: this.useCustomAttenuation, rolloffFactor: this.rolloffFactor,
-                refDistance: this.refDistance, distanceModel: this.distanceModel
+                autoplay: this.autoplay,
+                loop: this.loop,
+                volume: this._volume,
+                spatialSound: this.spatialSound,
+                maxDistance: this.maxDistance,
+                useCustomAttenuation: this.useCustomAttenuation,
+                rolloffFactor: this.rolloffFactor,
+                refDistance: this.refDistance,
+                distanceModel: this.distanceModel,
             };
 
             var clonedSound = new Sound(this.name + "_cloned", new ArrayBuffer(0), this._scene, null, currentOptions);
@@ -1042,7 +1074,7 @@ export class Sound {
             playbackRate: this._playbackRate,
             panningModel: this._panningModel,
             soundTrackId: this.soundTrackId,
-            metadata: this.metadata
+            metadata: this.metadata,
         };
 
         if (this.spatialSound) {
@@ -1078,34 +1110,44 @@ export class Sound {
 
         if (parsedSound.url) {
             soundUrl = rootUrl + parsedSound.url;
-        }
-        else {
+        } else {
             soundUrl = rootUrl + soundName;
         }
 
         var options = {
-            autoplay: parsedSound.autoplay, loop: parsedSound.loop, volume: parsedSound.volume,
-            spatialSound: parsedSound.spatialSound, maxDistance: parsedSound.maxDistance,
+            autoplay: parsedSound.autoplay,
+            loop: parsedSound.loop,
+            volume: parsedSound.volume,
+            spatialSound: parsedSound.spatialSound,
+            maxDistance: parsedSound.maxDistance,
             rolloffFactor: parsedSound.rolloffFactor,
             refDistance: parsedSound.refDistance,
             distanceModel: parsedSound.distanceModel,
-            playbackRate: parsedSound.playbackRate
+            playbackRate: parsedSound.playbackRate,
         };
 
         var newSound: Sound;
 
         if (!sourceSound) {
-            newSound = new Sound(soundName, soundUrl, scene, () => { scene._removePendingData(newSound); }, options);
+            newSound = new Sound(
+                soundName,
+                soundUrl,
+                scene,
+                () => {
+                    scene._removePendingData(newSound);
+                },
+                options
+            );
             scene._addPendingData(newSound);
-        }
-        else {
+        } else {
             var setBufferAndRun = () => {
                 if (sourceSound._isReadyToPlay) {
                     newSound._audioBuffer = sourceSound.getAudioBuffer();
                     newSound._isReadyToPlay = true;
-                    if (newSound.autoplay) { newSound.play(0, newSound._offset, newSound._length); }
-                }
-                else {
+                    if (newSound.autoplay) {
+                        newSound.play(0, newSound._offset, newSound._length);
+                    }
+                } else {
                     window.setTimeout(setBufferAndRun, 300);
                 }
             };

+ 14 - 2
src/Cameras/Inputs/freeCameraTouchInput.ts

@@ -38,6 +38,18 @@ export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
     private _onLostFocus: Nullable<(e: FocusEvent) => any>;
 
     /**
+     * Manage the touch inputs to control the movement of a free camera.
+     * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * @param allowMouse Defines if mouse events can be treated as touch events
+     */
+    constructor(
+        /**
+         * Define if mouse events can be treated as touch events
+         */
+        public allowMouse = false) {
+    }
+
+    /**
      * Attach the input controls to a specific dom element to get the input from.
      * @param element Defines the element the controls should be listened from
      * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
@@ -54,8 +66,8 @@ export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
             this._pointerInput = (p) => {
                 var evt = <PointerEvent>p.event;
 
-                let isMouseEvent = evt instanceof MouseEvent;
-                if (evt.pointerType === "mouse" || isMouseEvent) {
+                let isMouseEvent = !this.camera.getEngine().hostInformation.isMobile && evt instanceof MouseEvent;
+                if (!this.allowMouse && (evt.pointerType === "mouse" || isMouseEvent)) {
                     return;
                 }
 

+ 2 - 0
src/Cameras/targetCamera.ts

@@ -247,6 +247,8 @@ export class TargetCamera extends Camera {
             this.position.z += Epsilon;
         }
 
+        this._referencePoint.normalize().scaleInPlace(this._initialFocalDistance);
+
         Matrix.LookAtLHToRef(this.position, target, this._defaultUp, this._camMatrix);
         this._camMatrix.invert();
 

+ 3 - 0
src/Cameras/touchCamera.ts

@@ -81,9 +81,12 @@ export class TouchCamera extends FreeCamera {
 
     /** @hidden */
     public _setupInputs() {
+        var touch = <FreeCameraTouchInput>this.inputs.attached["touch"];
         var mouse = <FreeCameraMouseInput>this.inputs.attached["mouse"];
         if (mouse) {
             mouse.touchEnabled = false;
+        } else {
+            touch.allowMouse = true;
         }
     }
 }

+ 21 - 2
src/Engines/thinEngine.ts

@@ -61,6 +61,16 @@ class BufferPointer {
     public buffer: WebGLBuffer;
 }
 
+/**
+ * Information about the current host
+ */
+export interface HostInformation {
+    /**
+     * Defines if the current host is a mobile
+     */
+    isMobile: boolean;
+}
+
 /** Interface defining initialization parameters for Engine class */
 export interface EngineOptions extends WebGLContextAttributes {
     /**
@@ -142,14 +152,14 @@ export class ThinEngine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.2.0-alpha.32";
+        return "babylonjs@4.2.0-alpha.33";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.2.0-alpha.32";
+        return "4.2.0-alpha.33";
     }
 
     /**
@@ -395,6 +405,13 @@ export class ThinEngine {
     /** @hidden */
     public _transformTextureUrl: Nullable<(url: string) => string> = null;
 
+    /**
+     * Gets information about the current host
+     */
+    public hostInformation: HostInformation = {
+        isMobile: false
+    };
+
     protected get _supportsHardwareTextureRescaling() {
         return false;
     }
@@ -538,6 +555,8 @@ export class ThinEngine {
             if (navigator && navigator.userAgent) {
                 let ua = navigator.userAgent;
 
+                this.hostInformation.isMobile = ua.indexOf("Mobile") !== -1;
+
                 for (var exception of ThinEngine.ExceptionList) {
                     let key = exception.key;
                     let targets = exception.targets;

+ 3 - 0
src/Gizmos/axisDragGizmo.ts

@@ -115,6 +115,9 @@ export class AxisDragGizmo extends Gizmo {
                 }
                 // Snapping logic
                 if (this.snapDistance == 0) {
+                    if ((this.attachedNode as any).position) { // Required for nodes like lights
+                        (this.attachedNode as any).position.addInPlaceFromFloats(localDelta.x, localDelta.y, localDelta.z);
+                    }
                     this.attachedNode.getWorldMatrix().addTranslationFromFloats(localDelta.x, localDelta.y, localDelta.z);
                     this.attachedNode.updateCache();
                 } else {

+ 0 - 5
src/Gizmos/lightGizmo.ts

@@ -149,11 +149,6 @@ export class LightGizmo extends Gizmo {
                 this._cachedForward.copyFrom(this.attachedMesh!.forward);
             }
         }
-        if (!this._light.isEnabled()) {
-            this._material.diffuseColor.set(0, 0, 0);
-        } else {
-            this._material.diffuseColor.set(this._light.diffuse.r / 3, this._light.diffuse.g / 3, this._light.diffuse.b / 3);
-        }
     }
 
     // Static helper methods

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 67 - 0
src/LibDeclarations/NativeExtensions/FeaturePoints.md


+ 65 - 59
src/LibDeclarations/webxr.d.ts

@@ -1,56 +1,20 @@
-type XRSessionMode =
-    | "inline"
-    | "immersive-vr"
-    | "immersive-ar";
-
-type XRReferenceSpaceType =
-    | "viewer"
-    | "local"
-    | "local-floor"
-    | "bounded-floor"
-    | "unbounded";
-
-type XREnvironmentBlendMode =
-    | "opaque"
-    | "additive"
-    | "alpha-blend";
-
-type XRVisibilityState =
-    | "visible"
-    | "visible-blurred"
-    | "hidden";
-
-type XRHandedness =
-    | "none"
-    | "left"
-    | "right";
-
-type XRTargetRayMode =
-    | "gaze"
-    | "tracked-pointer"
-    | "screen";
-
-type XREye =
-    | "none"
-    | "left"
-    | "right";
-
-type XREventType =
-    | "devicechange"
-    | "visibilitychange"
-    | "end"
-    | "inputsourceschange"
-    | "select"
-    | "selectstart"
-    | "selectend"
-    | "squeeze"
-    | "squeezestart"
-    | "squeezeend"
-    | "reset";
-
-interface XRSpace extends EventTarget {
+type XRSessionMode = "inline" | "immersive-vr" | "immersive-ar";
 
-}
+type XRReferenceSpaceType = "viewer" | "local" | "local-floor" | "bounded-floor" | "unbounded";
+
+type XREnvironmentBlendMode = "opaque" | "additive" | "alpha-blend";
+
+type XRVisibilityState = "visible" | "visible-blurred" | "hidden";
+
+type XRHandedness = "none" | "left" | "right";
+
+type XRTargetRayMode = "gaze" | "tracked-pointer" | "screen";
+
+type XREye = "none" | "left" | "right";
+
+type XREventType = "devicechange" | "visibilitychange" | "end" | "inputsourceschange" | "select" | "selectstart" | "selectend" | "squeeze" | "squeezestart" | "squeezeend" | "reset";
+
+interface XRSpace extends EventTarget {}
 
 interface XRRenderState {
     depthNear?: number;
@@ -66,6 +30,7 @@ interface XRInputSource {
     gripSpace: XRSpace | undefined;
     gamepad: Gamepad | undefined;
     profiles: Array<string>;
+    hand: XRHand | undefined;
 }
 
 interface XRSessionInit {
@@ -91,9 +56,7 @@ interface XRSession {
     requestHitTest(ray: XRRay, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]>;
 
     // legacy plane detection
-    updateWorldTrackingState(options: {
-        planeDetectionState?: { enabled: boolean; }
-    }): void;
+    updateWorldTrackingState(options: { planeDetectionState?: { enabled: boolean } }): void;
 }
 
 interface XRReferenceSpace extends XRSpace {
@@ -110,7 +73,7 @@ interface XRFrame {
     getPose(space: XRSpace, baseSpace: XRSpace): XRPose | undefined;
 
     // AR
-    getHitTestResults(hitTestSource: XRHitTestSource): Array<XRHitTestResult> ;
+    getHitTestResults(hitTestSource: XRHitTestSource): Array<XRHitTestResult>;
     getHitTestResultsForTransientInput(hitTestSource: XRTransientInputHitTestSource): Array<XRTransientInputHitTestResult>;
     // Anchors
     trackedAnchors?: XRAnchorSet;
@@ -119,6 +82,8 @@ interface XRFrame {
     worldInformation: {
         detectedPlanes?: XRPlaneSet;
     };
+    // Hand tracking
+    getJointPose(joint: XRJointSpace, baseSpace: XRSpace): XRJointPose;
 }
 
 interface XRViewerPose extends XRPose {
@@ -141,7 +106,7 @@ interface XRWebGLLayerOptions {
 
 declare var XRWebGLLayer: {
     prototype: XRWebGLLayer;
-    new(session: XRSession, context: WebGLRenderingContext | undefined, options?: XRWebGLLayerOptions): XRWebGLLayer;
+    new (session: XRSession, context: WebGLRenderingContext | undefined, options?: XRWebGLLayerOptions): XRWebGLLayer;
 };
 interface XRWebGLLayer {
     framebuffer: WebGLFramebuffer;
@@ -186,7 +151,7 @@ declare class XRRay {
 declare enum XRHitTestTrackableType {
     "point",
     "plane",
-    "mesh"
+    "mesh",
 }
 
 interface XRHitResult {
@@ -234,4 +199,45 @@ interface XRPlane {
     planeSpace: XRSpace;
     polygon: Array<DOMPointReadOnly>;
     lastChangedTime: number;
-}
+}
+
+interface XRJointSpace extends XRSpace {}
+
+interface XRJointPose extends XRPose {
+    radius: number | undefined;
+}
+
+declare class XRHand extends Array<XRJointSpace> {
+    readonly length: number;
+
+    static readonly WRIST = 0;
+
+    static readonly THUMB_METACARPAL = 1;
+    static readonly THUMB_PHALANX_PROXIMAL = 2;
+    static readonly THUMB_PHALANX_DISTAL = 3;
+    static readonly THUMB_PHALANX_TIP = 4;
+
+    static readonly INDEX_METACARPAL = 5;
+    static readonly INDEX_PHALANX_PROXIMAL = 6;
+    static readonly INDEX_PHALANX_INTERMEDIATE = 7;
+    static readonly INDEX_PHALANX_DISTAL = 8;
+    static readonly INDEX_PHALANX_TIP = 9;
+
+    static readonly MIDDLE_METACARPAL = 10;
+    static readonly MIDDLE_PHALANX_PROXIMAL = 11;
+    static readonly MIDDLE_PHALANX_INTERMEDIATE = 12;
+    static readonly MIDDLE_PHALANX_DISTAL = 13;
+    static readonly MIDDLE_PHALANX_TIP = 14;
+
+    static readonly RING_METACARPAL = 15;
+    static readonly RING_PHALANX_PROXIMAL = 16;
+    static readonly RING_PHALANX_INTERMEDIATE = 17;
+    static readonly RING_PHALANX_DISTAL = 18;
+    static readonly RING_PHALANX_TIP = 19;
+
+    static readonly LITTLE_METACARPAL = 20;
+    static readonly LITTLE_PHALANX_PROXIMAL = 21;
+    static readonly LITTLE_PHALANX_INTERMEDIATE = 22;
+    static readonly LITTLE_PHALANX_DISTAL = 23;
+    static readonly LITTLE_PHALANX_TIP = 24;
+}

+ 11 - 0
src/LibDeclarations/webxr.nativeextensions.d.ts

@@ -0,0 +1,11 @@
+// This file contains native only extensions for WebXR  These APIs are not supported in the browser yet.
+// They are intended for use with either Babylon Native https://github.com/BabylonJS/BabylonNative or
+// Babylon React Native: https://github.com/BabylonJS/BabylonReactNative
+
+interface XRSession {
+    trySetFeaturePointCloudEnabled(enabled: boolean): boolean;
+}
+
+interface XRFrame {
+    featurePointCloud? : Array<number>;
+}

+ 2 - 0
src/Lights/Shadows/cascadedShadowGenerator.ts

@@ -826,6 +826,8 @@ export class CascadedShadowGenerator extends ShadowGenerator {
 
         const world = mesh.getWorldMatrix();
 
+        effect.setMatrix(matriceNames?.world ?? "world", world);
+
         world.multiplyToRef(this.getCascadeTransformMatrix(this._currentLayer)!, tmpMatrix);
 
         effect.setMatrix(matriceNames?.worldViewProjection ?? "worldViewProjection", tmpMatrix);

+ 10 - 7
src/Meshes/Builders/sphereBuilder.ts

@@ -48,13 +48,16 @@ VertexData.CreateSphere = function(options: { segments?: number, diameter?: numb
         if (zRotationStep > 0) {
             var verticesCount = positions.length / 3;
             for (var firstIndex = verticesCount - 2 * (totalYRotationSteps + 1); (firstIndex + totalYRotationSteps + 2) < verticesCount; firstIndex++) {
-                indices.push((firstIndex));
-                indices.push((firstIndex + 1));
-                indices.push(firstIndex + totalYRotationSteps + 1);
-
-                indices.push((firstIndex + totalYRotationSteps + 1));
-                indices.push((firstIndex + 1));
-                indices.push((firstIndex + totalYRotationSteps + 2));
+                if (zRotationStep > 1) {
+                    indices.push((firstIndex));
+                    indices.push((firstIndex + 1));
+                    indices.push(firstIndex + totalYRotationSteps + 1);
+                }
+                if (zRotationStep < totalZRotationSteps) {
+                    indices.push((firstIndex + totalYRotationSteps + 1));
+                    indices.push((firstIndex + 1));
+                    indices.push((firstIndex + totalYRotationSteps + 2));
+                }
             }
         }
     }

+ 1 - 1
src/Meshes/thinInstanceMesh.ts

@@ -301,7 +301,7 @@ Mesh.prototype.thinInstanceRefreshBoundingInfo = function(forceRefreshParentInfo
         }
     }
 
-    boundingInfo.reConstruct(TmpVectors.Vector3[0], TmpVectors.Vector3[1]);
+    boundingInfo.reConstruct(TmpVectors.Vector3[0], TmpVectors.Vector3[1], this.getWorldMatrix());
 };
 
 Mesh.prototype._thinInstanceUpdateBufferSize = function(kind: string, numInstances: number = 1) {

+ 3 - 2
src/Misc/screenshotTools.ts

@@ -113,8 +113,9 @@ export class ScreenshotTools {
      * @param antialiasing Whether antialiasing should be turned on or not (default: false)
      * @param fileName A name for for the downloaded file.
      * @param renderSprites Whether the sprites should be rendered or not (default: false)
+     * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
      */
-    public static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType: string = "image/png", samples: number = 1, antialiasing: boolean = false, fileName?: string, renderSprites: boolean = false): void {
+    public static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType: string = "image/png", samples: number = 1, antialiasing: boolean = false, fileName?: string, renderSprites: boolean = false, enableStencilBuffer: boolean = false): void {
         const { height, width } = ScreenshotTools._getScreenshotSize(engine, camera, size);
         let targetTextureSize = { width, height };
 
@@ -142,7 +143,7 @@ export class ScreenshotTools {
         scene.render();
 
         // At this point size can be a number, or an object (according to engine.prototype.createRenderTargetTexture method)
-        var texture = new RenderTargetTexture("screenShot", targetTextureSize, scene, false, false, Constants.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE);
+        var texture = new RenderTargetTexture("screenShot", targetTextureSize, scene, false, false, Constants.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE, undefined, enableStencilBuffer);
         texture.renderList = null;
         texture.samples = samples;
         texture.renderSprites = renderSprites;

+ 1 - 0
src/Rendering/outlineRenderer.ts

@@ -183,6 +183,7 @@ export class OutlineRenderer implements ISceneComponent {
         this._effect.setFloat("offset", useOverlay ? 0 : renderingMesh.outlineWidth);
         this._effect.setColor4("color", useOverlay ? renderingMesh.overlayColor : renderingMesh.outlineColor, useOverlay ? renderingMesh.overlayAlpha : material.alpha);
         this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
+        this._effect.setMatrix("world", effectiveMesh.getWorldMatrix());
 
         // Bones
         if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {

+ 2 - 0
src/Sprites/spriteManager.ts

@@ -780,6 +780,8 @@ export class SpriteManager implements ISpriteManager {
         if (this._scene.useRightHandedSystem) {
             engine.setState(culling, zOffset, false, true);
         }
+
+        engine.unbindInstanceAttributes();
     }
 
     /**

+ 4 - 0
src/XR/features/WebXRControllerPointerSelection.ts

@@ -85,6 +85,10 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature {
             // already attached
             return;
         }
+        // For now no support for hand-tracked input sources!
+        if (xrController.inputSource.hand && !xrController.inputSource.gamepad) {
+            return;
+        }
         // only support tracker pointer
         const { laserPointer, selectionMesh } = this._generateNewMeshPair(xrController.pointer);
 

+ 1 - 1
src/XR/features/WebXRControllerTeleportation.ts

@@ -343,7 +343,7 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
             }
             targetMesh.rotationQuaternion = targetMesh.rotationQuaternion || new Quaternion();
             const controllerData = this._controllers[this._currentTeleportationControllerId];
-            if (controllerData.teleportationState.forward) {
+            if (controllerData && controllerData.teleportationState.forward) {
                 // set the rotation
                 Quaternion.RotationYawPitchRollToRef(controllerData.teleportationState.currentRotation + controllerData.teleportationState.baseRotation, 0, 0, targetMesh.rotationQuaternion);
                 // set the ray and position

+ 170 - 0
src/XR/features/WebXRFeaturePointSystem.ts

@@ -0,0 +1,170 @@
+import { WebXRFeaturesManager, WebXRFeatureName } from "../webXRFeaturesManager";
+import { WebXRSessionManager } from "../webXRSessionManager";
+import { Observable } from "../../Misc/observable";
+import { Vector3 } from "../../Maths/math.vector";
+import { WebXRAbstractFeature } from "./WebXRAbstractFeature";
+
+/**
+ * A babylon interface for a "WebXR" feature point.
+ * Represents the position and confidence value of a given feature point.
+ */
+export interface IWebXRFeaturePoint {
+    /**
+     * Represents the position of the feature point in world space.
+     */
+    position : Vector3;
+    /**
+     * Represents the confidence value of the feature point in world space. 0 being least confident, and 1 being most confident.
+     */
+    confidenceValue : number;
+}
+
+/**
+ * The feature point system is used to detect feature points from real world geometry.
+ * This feature is currently experimental and only supported on BabylonNative, and should not be used in the browser.
+ * The newly introduced API can be seen in webxr.nativeextensions.d.ts and described in FeaturePoints.md.
+ */
+export class WebXRFeaturePointSystem extends WebXRAbstractFeature {
+    private _enabled: boolean = false;
+    private _featurePointCloud: Array<IWebXRFeaturePoint> = [];
+
+    /**
+     * The module's name
+     */
+    public static readonly Name = WebXRFeatureName.FEATURE_POINTS;
+    /**
+     * The (Babylon) version of this module.
+     * This is an integer representing the implementation version.
+     * This number does not correspond to the WebXR specs version
+     */
+    public static readonly Version = 1;
+     /**
+     * Observers registered here will be executed whenever new feature points are added (on XRFrame while the session is tracking).
+     * Will notify the observers about which feature points have been added.
+     */
+    public readonly onFeaturePointsAddedObservable: Observable<number[]> = new Observable();
+    /**
+     * Observers registered here will be executed whenever a feature point has been updated (on XRFrame while the session is tracking).
+     * Will notify the observers about which feature points have been updated.
+     */
+    public readonly onFeaturePointsUpdatedObservable: Observable<number[]> = new Observable();
+    /**
+     * The current feature point cloud maintained across frames.
+     */
+    public get featurePointCloud() : Array<IWebXRFeaturePoint> {
+        return this._featurePointCloud;
+    }
+
+    /**
+     * construct the feature point system
+     * @param _xrSessionManager an instance of xr Session manager
+     */
+    constructor(_xrSessionManager: WebXRSessionManager) {
+        super(_xrSessionManager);
+        this.xrNativeFeatureName = "bjsfeature-points";
+        if (this._xrSessionManager.session) {
+            this._init();
+        } else {
+            this._xrSessionManager.onXRSessionInit.addOnce(() => {
+                this._init();
+            });
+        }
+    }
+
+    /**
+     * Detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    public detach(): boolean {
+        if (!super.detach()) {
+            return false;
+        }
+
+        this.featurePointCloud.length = 0;
+        return true;
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    public dispose(): void {
+        super.dispose();
+
+        this._featurePointCloud.length = 0;
+        this.onFeaturePointsUpdatedObservable.clear();
+        this.onFeaturePointsAddedObservable.clear();
+    }
+
+    /**
+     * On receiving a new XR frame if this feature is attached notify observers new feature point data is available.
+     */
+    protected _onXRFrame(frame: XRFrame) {
+        if (!this.attached || !this._enabled || !frame) {
+            return;
+        }
+
+        const featurePointRawData: number[] | undefined = frame.featurePointCloud;
+        if (!featurePointRawData || featurePointRawData.length === 0) {
+            return;
+        } else {
+            if (featurePointRawData.length % 5 !== 0) {
+                throw new Error("Received malformed feature point cloud of length: " + featurePointRawData.length);
+            }
+
+            const numberOfFeaturePoints : number = featurePointRawData.length / 5;
+            let updatedFeaturePoints = new Array();
+            let addedFeaturePoints = new Array();
+            for (var i = 0; i < numberOfFeaturePoints; i++) {
+                const rawIndex: number = i * 5;
+                const id = featurePointRawData[rawIndex + 4];
+
+                // IDs should be durable across frames and strictly increasing from 0 up, so use them as indexing into the feature point array.
+                if (!this._featurePointCloud[id]) {
+                    this._featurePointCloud[id] = { position: new Vector3(), confidenceValue: 0 };
+                    addedFeaturePoints.push(id);
+                } else {
+                    updatedFeaturePoints.push(id);
+                }
+
+                // Set the feature point values.
+                this._featurePointCloud[id].position.x = featurePointRawData[rawIndex];
+                this._featurePointCloud[id].position.y = featurePointRawData[rawIndex + 1];
+                this._featurePointCloud[id].position.z = featurePointRawData[rawIndex + 2];
+                this._featurePointCloud[id].confidenceValue = featurePointRawData[rawIndex + 3];
+            }
+
+            // Signal observers that feature points have been added if necessary.
+            if (addedFeaturePoints.length > 0) {
+                this.onFeaturePointsAddedObservable.notifyObservers(addedFeaturePoints);
+            }
+
+            // Signal observers that feature points have been updated if necessary.
+            if (updatedFeaturePoints.length > 0) {
+                this.onFeaturePointsUpdatedObservable.notifyObservers(updatedFeaturePoints);
+            }
+        }
+    }
+
+    /**
+     * Initializes the feature. If the feature point feature is not available for this environment do not mark the feature as enabled.
+     */
+    private _init() {
+        if (!this._xrSessionManager.session.trySetFeaturePointCloudEnabled || !this._xrSessionManager.session.trySetFeaturePointCloudEnabled(true)) {
+            // fail silently
+            return;
+        }
+
+        this._enabled = true;
+    }
+}
+
+// register the plugin
+WebXRFeaturesManager.AddWebXRFeature(
+    WebXRFeaturePointSystem.Name,
+    (xrSessionManager) => {
+        return () => new WebXRFeaturePointSystem(xrSessionManager);
+    },
+    WebXRFeaturePointSystem.Version
+);

+ 366 - 0
src/XR/features/WebXRHandTracking.ts

@@ -0,0 +1,366 @@
+import { WebXRAbstractFeature } from "./WebXRAbstractFeature";
+import { WebXRSessionManager } from "../webXRSessionManager";
+import { WebXRFeatureName } from "../webXRFeaturesManager";
+import { AbstractMesh } from "../../Meshes/abstractMesh";
+import { Mesh } from "../../Meshes/mesh";
+import { SphereBuilder } from "../../Meshes/Builders/sphereBuilder";
+import { WebXRInput } from "../webXRInput";
+import { WebXRInputSource } from "../webXRInputSource";
+import { Quaternion } from "../../Maths/math.vector";
+import { Nullable } from "../../types";
+import { PhysicsImpostor } from "../../Physics/physicsImpostor";
+import { WebXRFeaturesManager } from "../webXRFeaturesManager";
+import { IDisposable } from "../../scene";
+import { Observable } from "../../Misc/observable";
+
+/**
+ * Configuration interface for the hand tracking feature
+ */
+export interface IWebXRHandTrackingOptions {
+    /**
+     * The xrInput that will be used as source for new hands
+     */
+    xrInput: WebXRInput;
+
+    /**
+     * Configuration object for the joint meshes
+     */
+    jointMeshes?: {
+        /**
+         * Should the meshes created be invisible (defaults to false)
+         */
+        invisible?: boolean;
+        /**
+         * A source mesh to be used to create instances. Defaults to a sphere.
+         * This mesh will be the source for all other (25) meshes.
+         * It should have the general size of a single unit, as the instances will be scaled according to the provided radius
+         */
+        sourceMesh?: Mesh;
+        /**
+         * Should the source mesh stay visible. Defaults to false
+         */
+        keepOriginalVisible?: boolean;
+        /**
+         * Scale factor for all instances (defaults to 2)
+         */
+        scaleFactor?: number;
+        /**
+         * Should each instance have its own physics impostor
+         */
+        enablePhysics?: boolean;
+        /**
+         * If enabled, override default physics properties
+         */
+        physicsProps?: { friction?: number; restitution?: number; impostorType?: number };
+        /**
+         * For future use - a single hand-mesh that will be updated according to the XRHand data provided
+         */
+        handMesh?: AbstractMesh;
+    };
+}
+
+/**
+ * Parts of the hands divided to writs and finger names
+ */
+export const enum HandPart {
+    /**
+     * HandPart - Wrist
+     */
+    WRIST = "wrist",
+    /**
+     * HandPart - The THumb
+     */
+    THUMB = "thumb",
+    /**
+     * HandPart - Index finger
+     */
+    INDEX = "index",
+    /**
+     * HandPart - Middle finger
+     */
+    MIDDLE = "middle",
+    /**
+     * HandPart - Ring finger
+     */
+    RING = "ring",
+    /**
+     * HandPart - Little finger
+     */
+    LITTLE = "little",
+}
+
+/**
+ * Representing a single hand (with its corresponding native XRHand object)
+ */
+export class WebXRHand implements IDisposable {
+    /**
+     * Hand-parts definition (key is HandPart)
+     */
+    public static HandPartsDefinition: { [key: string]: number[] };
+
+    /**
+     * Populate the HandPartsDefinition object.
+     * This is called as a side effect since certain browsers don't have XRHand defined.
+     */
+    public static _PopulateHandPartsDefinition() {
+        if (typeof XRHand !== "undefined") {
+            WebXRHand.HandPartsDefinition = {
+                [HandPart.WRIST]: [XRHand.WRIST],
+                [HandPart.THUMB]: [XRHand.THUMB_METACARPAL, XRHand.THUMB_PHALANX_PROXIMAL, XRHand.THUMB_PHALANX_DISTAL, XRHand.THUMB_PHALANX_TIP],
+                [HandPart.INDEX]: [XRHand.INDEX_METACARPAL, XRHand.INDEX_PHALANX_PROXIMAL, XRHand.INDEX_PHALANX_INTERMEDIATE, XRHand.INDEX_PHALANX_DISTAL, XRHand.INDEX_PHALANX_TIP],
+                [HandPart.MIDDLE]: [XRHand.MIDDLE_METACARPAL, XRHand.MIDDLE_PHALANX_PROXIMAL, XRHand.MIDDLE_PHALANX_INTERMEDIATE, XRHand.MIDDLE_PHALANX_DISTAL, XRHand.MIDDLE_PHALANX_TIP],
+                [HandPart.RING]: [XRHand.RING_METACARPAL, XRHand.RING_PHALANX_PROXIMAL, XRHand.RING_PHALANX_INTERMEDIATE, XRHand.RING_PHALANX_DISTAL, XRHand.RING_PHALANX_TIP],
+                [HandPart.LITTLE]: [XRHand.LITTLE_METACARPAL, XRHand.LITTLE_PHALANX_PROXIMAL, XRHand.LITTLE_PHALANX_INTERMEDIATE, XRHand.LITTLE_PHALANX_DISTAL, XRHand.LITTLE_PHALANX_TIP],
+            };
+        }
+    }
+
+    /**
+     * Construct a new hand object
+     * @param xrController the controller to which the hand correlates
+     * @param trackedMeshes the meshes to be used to track the hand joints
+     */
+    constructor(
+        /** the controller to which the hand correlates */
+        public readonly xrController: WebXRInputSource,
+        /** the meshes to be used to track the hand joints */
+        public readonly trackedMeshes: AbstractMesh[]) {}
+
+    /**
+     * Update this hand from the latest xr frame
+     * @param xrFrame xrFrame to update from
+     * @param referenceSpace The current viewer reference space
+     * @param scaleFactor optional scale factor for the meshes
+     */
+    public updateFromXRFrame(xrFrame: XRFrame, referenceSpace: XRReferenceSpace, scaleFactor: number = 2) {
+        const hand = this.xrController.inputSource.hand as XRJointSpace[];
+        if (!hand) {
+            return;
+        }
+        this.trackedMeshes.forEach((mesh, idx) => {
+            const xrJoint = hand[idx];
+            if (xrJoint) {
+                let pose = xrFrame.getJointPose(xrJoint, referenceSpace);
+                if (!pose || !pose.transform) {
+                    return;
+                }
+                // get the transformation. can be done with matrix decomposition as well
+                const pos = pose.transform.position;
+                const orientation = pose.transform.orientation;
+                mesh.position.set(pos.x, pos.y, pos.z);
+                mesh.rotationQuaternion!.set(orientation.x, orientation.y, orientation.z, orientation.w);
+                // left handed system conversion
+                if (!mesh.getScene().useRightHandedSystem) {
+                    mesh.position.z *= -1;
+                    mesh.rotationQuaternion!.z *= -1;
+                    mesh.rotationQuaternion!.w *= -1;
+                }
+                // get the radius of the joint. In general it is static, but just in case it does change we update it on each frame.
+                const radius = (pose.radius || 0.008) * scaleFactor;
+                mesh.scaling.set(radius, radius, radius);
+            }
+        });
+    }
+
+    /**
+     * Get meshes of part of the hand
+     * @param part the part of hand to get
+     * @returns An array of meshes that correlate to the hand part requested
+     */
+    public getHandPartMeshes(part: HandPart): AbstractMesh[] {
+        return WebXRHand.HandPartsDefinition[part].map((idx) => this.trackedMeshes[idx]);
+    }
+
+    /**
+     * Dispose this Hand object
+     */
+    public dispose() {
+        this.trackedMeshes.forEach((mesh) => mesh.dispose());
+    }
+}
+
+// Populate the hand parts definition
+WebXRHand._PopulateHandPartsDefinition();
+
+/**
+ * WebXR Hand Joint tracking feature, available for selected browsers and devices
+ */
+export class WebXRHandTracking extends WebXRAbstractFeature {
+    private static _idCounter = 0;
+    /**
+     * The module's name
+     */
+    public static readonly Name = WebXRFeatureName.HAND_TRACKING;
+    /**
+     * The (Babylon) version of this module.
+     * This is an integer representing the implementation version.
+     * This number does not correspond to the WebXR specs version
+     */
+    public static readonly Version = 1;
+
+    /**
+     * This observable will notify registered observers when a new hand object was added and initialized
+     */
+    public onHandAddedObservable: Observable<WebXRHand> = new Observable();
+    /**
+     * This observable will notify its observers right before the hand object is disposed
+     */
+    public onHandRemovedObservable: Observable<WebXRHand> = new Observable();
+
+    private _hands: {
+        [controllerId: string]: {
+            id: number;
+            handObject: WebXRHand;
+        };
+    } = {};
+
+    /**
+     * Creates a new instance of the hit test feature
+     * @param _xrSessionManager an instance of WebXRSessionManager
+     * @param options options to use when constructing this feature
+     */
+    constructor(
+        _xrSessionManager: WebXRSessionManager,
+        /**
+         * options to use when constructing this feature
+         */
+        public readonly options: IWebXRHandTrackingOptions
+    ) {
+        super(_xrSessionManager);
+        this.xrNativeFeatureName = "hand-tracking";
+    }
+
+    /**
+     * Check if the needed objects are defined.
+     * This does not mean that the feature is enabled, but that the objects needed are well defined.
+     */
+    public isCompatible(): boolean {
+        return typeof XRHand !== "undefined";
+    }
+
+    /**
+     * attach this feature
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    public attach(): boolean {
+        if (!super.attach()) {
+            return false;
+        }
+        this.options.xrInput.controllers.forEach(this._attachHand);
+        this._addNewAttachObserver(this.options.xrInput.onControllerAddedObservable, this._attachHand);
+        this._addNewAttachObserver(this.options.xrInput.onControllerRemovedObservable, (controller) => {
+            // REMOVE the controller
+            this._detachHand(controller.uniqueId);
+        });
+
+        return true;
+    }
+
+    /**
+     * detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    public detach(): boolean {
+        if (!super.detach()) {
+            return false;
+        }
+
+        Object.keys(this._hands).forEach((controllerId) => {
+            this._detachHand(controllerId);
+        });
+
+        return true;
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    public dispose(): void {
+        super.dispose();
+        this.onHandAddedObservable.clear();
+    }
+
+    /**
+     * Get the hand object according to the controller id
+     * @param controllerId the controller id to which we want to get the hand
+     * @returns null if not found or the WebXRHand object if found
+     */
+    public getHandByControllerId(controllerId: string): Nullable<WebXRHand> {
+        return this._hands[controllerId]?.handObject || null;
+    }
+
+    /**
+     * Get a hand object according to the requested handedness
+     * @param handedness the handedness to request
+     * @returns null if not found or the WebXRHand object if found
+     */
+    public getHandByHandedness(handedness: XRHandedness): Nullable<WebXRHand> {
+        const handednesses = Object.keys(this._hands).map((key) => this._hands[key].handObject.xrController.inputSource.handedness);
+        const found = handednesses.indexOf(handedness);
+        if (found !== -1) {
+            return this._hands[found].handObject;
+        }
+        return null;
+    }
+
+    protected _onXRFrame(_xrFrame: XRFrame): void {
+        // iterate over the hands object
+        Object.keys(this._hands).forEach((id) => {
+            this._hands[id].handObject.updateFromXRFrame(_xrFrame, this._xrSessionManager.referenceSpace, this.options.jointMeshes?.scaleFactor);
+        });
+    }
+
+    private _attachHand = (xrController: WebXRInputSource) => {
+        if (!xrController.inputSource.hand || this._hands[xrController.uniqueId]) {
+            // already attached
+            return;
+        }
+
+        const hand = xrController.inputSource.hand;
+        const trackedMeshes: AbstractMesh[] = [];
+        const originalMesh = this.options.jointMeshes?.sourceMesh || SphereBuilder.CreateSphere("jointParent", { diameter: 1 });
+        originalMesh.isVisible = !!this.options.jointMeshes?.keepOriginalVisible;
+        for (let i = 0; i < hand.length; ++i) {
+            const newInstance = originalMesh.createInstance(`${xrController.uniqueId}-handJoint-${i}`);
+            newInstance.isPickable = false;
+            if (this.options.jointMeshes?.enablePhysics) {
+                const props = this.options.jointMeshes.physicsProps || {};
+                const type = props.impostorType !== undefined ? props.impostorType : PhysicsImpostor.SphereImpostor;
+                newInstance.physicsImpostor = new PhysicsImpostor(newInstance, type, { mass: 0, ...props });
+            }
+            newInstance.rotationQuaternion = new Quaternion();
+            trackedMeshes.push(newInstance);
+        }
+
+        const webxrHand = new WebXRHand(xrController, trackedMeshes);
+
+        // get two new meshes
+        this._hands[xrController.uniqueId] = {
+            handObject: webxrHand,
+            id: WebXRHandTracking._idCounter++,
+        };
+
+        this.onHandAddedObservable.notifyObservers(webxrHand);
+    };
+
+    private _detachHand(controllerId: string) {
+        if (this._hands[controllerId]) {
+            this.onHandRemovedObservable.notifyObservers(this._hands[controllerId].handObject);
+            this._hands[controllerId].handObject.dispose();
+        }
+    }
+}
+
+//register the plugin
+WebXRFeaturesManager.AddWebXRFeature(
+    WebXRHandTracking.Name,
+    (xrSessionManager, options) => {
+        return () => new WebXRHandTracking(xrSessionManager, options);
+    },
+    WebXRHandTracking.Version,
+    false
+);

+ 2 - 0
src/XR/features/index.ts

@@ -6,3 +6,5 @@ export * from "./WebXRControllerTeleportation";
 export * from "./WebXRControllerPointerSelection";
 export * from "./WebXRControllerPhysics";
 export * from "./WebXRHitTest";
+export * from "./WebXRFeaturePointSystem";
+export * from "./WebXRHandTracking";

+ 6 - 6
src/XR/motionController/webXRAbstractMotionController.ts

@@ -186,7 +186,7 @@ export interface IMotionControllerMeshMap {
     /**
      * The mesh that will be changed when axis value changes
      */
-    valueMesh: AbstractMesh;
+    valueMesh?: AbstractMesh;
 }
 
 /**
@@ -431,13 +431,13 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
     }
 
     // Look through all children recursively. This will return null if no mesh exists with the given name.
-    protected _getChildByName(node: AbstractMesh, name: string): AbstractMesh {
-        return <AbstractMesh>node.getChildren((n) => n.name === name, false)[0];
+    protected _getChildByName(node: AbstractMesh, name: string): AbstractMesh | undefined {
+        return <AbstractMesh | undefined>node.getChildren((n) => n.name === name, false)[0];
     }
 
     // Look through only immediate children. This will return null if no mesh exists with the given name.
-    protected _getImmediateChildByName(node: AbstractMesh, name: string): AbstractMesh {
-        return <AbstractMesh>node.getChildren((n) => n.name == name, true)[0];
+    protected _getImmediateChildByName(node: AbstractMesh, name: string): AbstractMesh | undefined {
+        return <AbstractMesh | undefined>node.getChildren((n) => n.name == name, true)[0];
     }
 
     /**
@@ -447,7 +447,7 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      * @hidden
      */
     protected _lerpTransform(axisMap: IMotionControllerMeshMap, axisValue: number, fixValueCoordinates?: boolean): void {
-        if (!axisMap.minMesh || !axisMap.maxMesh) {
+        if (!axisMap.minMesh || !axisMap.maxMesh || !axisMap.valueMesh) {
             return;
         }
 

+ 6 - 3
src/XR/motionController/webXRProfiledMotionController.ts

@@ -17,7 +17,7 @@ import { Logger } from "../../Misc/logger";
 export class WebXRProfiledMotionController extends WebXRAbstractMotionController {
     private _buttonMeshMapping: {
         [buttonName: string]: {
-            mainMesh: AbstractMesh;
+            mainMesh?: AbstractMesh;
             states: {
                 [state: string]: IMotionControllerMeshMap;
             };
@@ -89,7 +89,7 @@ export class WebXRProfiledMotionController extends WebXRAbstractMotionController
                         );
                         dot.material = new StandardMaterial(visualResponseKey + "mat", this.scene);
                         (<StandardMaterial>dot.material).diffuseColor = Color3.Red();
-                        dot.parent = this._buttonMeshMapping[type].states[visualResponseKey].valueMesh;
+                        dot.parent = this._buttonMeshMapping[type].states[visualResponseKey].valueMesh || null;
                         dot.isVisible = false;
                         this._touchDots[visualResponseKey] = dot;
                     }
@@ -145,7 +145,10 @@ export class WebXRProfiledMotionController extends WebXRAbstractMotionController
                     this._lerpTransform(meshes.states[visualResponseKey], value, visResponse.componentProperty !== "button");
                 } else {
                     // visibility
-                    meshes.states[visualResponseKey].valueMesh.isVisible = component.touched || component.pressed;
+                    const valueMesh = meshes.states[visualResponseKey].valueMesh;
+                    if (valueMesh) {
+                        valueMesh.isVisible = component.touched || component.pressed;
+                    }
                     if (this._touchDots[visualResponseKey]) {
                         this._touchDots[visualResponseKey].isVisible = component.touched || component.pressed;
                     }

+ 11 - 7
src/XR/webXREnterExitUI.ts

@@ -69,7 +69,10 @@ export class WebXREnterExitUIOptions {
 export class WebXREnterExitUI implements IDisposable {
     private _activeButton: Nullable<WebXREnterExitUIButton> = null;
     private _buttons: Array<WebXREnterExitUIButton> = [];
-    private _overlay: HTMLDivElement;
+    /**
+     * The HTML Div Element to which buttons are added.
+     */
+    public readonly overlay: HTMLDivElement;
 
     /**
      * Fired every time the active button is changed.
@@ -90,8 +93,9 @@ export class WebXREnterExitUI implements IDisposable {
         /** version of the options passed to this UI */
         public options: WebXREnterExitUIOptions
     ) {
-        this._overlay = document.createElement("div");
-        this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
+        this.overlay = document.createElement("div");
+        this.overlay.classList.add('xr-button-overlay');
+        this.overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
 
         // if served over HTTP, warn people.
         // Hopefully the browsers will catch up
@@ -131,7 +135,7 @@ export class WebXREnterExitUI implements IDisposable {
 
         var renderCanvas = scene.getEngine().getInputElement();
         if (renderCanvas && renderCanvas.parentNode) {
-            renderCanvas.parentNode.appendChild(this._overlay);
+            renderCanvas.parentNode.appendChild(this.overlay);
             scene.onDisposeObservable.addOnce(() => {
                 this.dispose();
             });
@@ -158,7 +162,7 @@ export class WebXREnterExitUI implements IDisposable {
         return Promise.all(supportedPromises).then((results) => {
             results.forEach((supported, i) => {
                 if (supported) {
-                    ui._overlay.appendChild(ui._buttons[i].element);
+                    ui.overlay.appendChild(ui._buttons[i].element);
                     ui._buttons[i].element.onclick = async () => {
                         if (helper.state == WebXRState.IN_XR) {
                             await helper.exitXRAsync();
@@ -192,8 +196,8 @@ export class WebXREnterExitUI implements IDisposable {
      */
     public dispose() {
         var renderCanvas = this.scene.getEngine().getInputElement();
-        if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
-            renderCanvas.parentNode.removeChild(this._overlay);
+        if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this.overlay)) {
+            renderCanvas.parentNode.removeChild(this.overlay);
         }
         this.activeButtonChangedObservable.clear();
     }

+ 26 - 7
src/XR/webXRFeaturesManager.ts

@@ -43,6 +43,11 @@ export interface IWebXRFeature extends IDisposable {
      * The name of the native xr feature name, if applicable (like anchor, hit-test, or hand-tracking)
      */
     xrNativeFeatureName?: string;
+
+    /**
+     * A list of (Babylon WebXR) features this feature depends on
+     */
+    dependsOn?: string[];
 }
 
 /**
@@ -52,31 +57,39 @@ export class WebXRFeatureName {
     /**
      * The name of the anchor system feature
      */
-    public static ANCHOR_SYSTEM = "xr-anchor-system";
+    public static readonly ANCHOR_SYSTEM = "xr-anchor-system";
     /**
      * The name of the background remover feature
      */
-    public static BACKGROUND_REMOVER = "xr-background-remover";
+    public static readonly BACKGROUND_REMOVER = "xr-background-remover";
     /**
      * The name of the hit test feature
      */
-    public static HIT_TEST = "xr-hit-test";
+    public static readonly HIT_TEST = "xr-hit-test";
     /**
      * physics impostors for xr controllers feature
      */
-    public static PHYSICS_CONTROLLERS = "xr-physics-controller";
+    public static readonly PHYSICS_CONTROLLERS = "xr-physics-controller";
     /**
      * The name of the plane detection feature
      */
-    public static PLANE_DETECTION = "xr-plane-detection";
+    public static readonly PLANE_DETECTION = "xr-plane-detection";
     /**
      * The name of the pointer selection feature
      */
-    public static POINTER_SELECTION = "xr-controller-pointer-selection";
+    public static readonly POINTER_SELECTION = "xr-controller-pointer-selection";
     /**
      * The name of the teleportation feature
      */
-    public static TELEPORTATION = "xr-controller-teleportation";
+    public static readonly TELEPORTATION = "xr-controller-teleportation";
+    /**
+     * The name of the feature points feature.
+     */
+    public static readonly FEATURE_POINTS = "xr-feature-points";
+    /**
+     * The name of the hand tracking feature.
+     */
+    public static readonly HAND_TRACKING = "xr-hand-tracking";
 }
 
 /**
@@ -308,6 +321,12 @@ export class WebXRFeaturesManager implements IDisposable {
         }
 
         const constructed = constructFunction();
+        if (constructed.dependsOn) {
+            const dependentsFound = constructed.dependsOn.every((featureName) => !!this._features[featureName]);
+            if (!dependentsFound) {
+                throw new Error(`Dependant features missing. Make sure the following features are enabled - ${constructed.dependsOn.join(", ")}`);
+            }
+        }
         if (constructed.isCompatible()) {
             this._features[name] = {
                 featureImplementation: constructed,

+ 6 - 0
src/XR/webXRInputSource.ts

@@ -40,6 +40,7 @@ export interface IWebXRControllerOptions {
 export class WebXRInputSource {
     private _tmpVector = new Vector3();
     private _uniqueId: string;
+    private _disposed = false;
 
     /**
      * 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
@@ -116,6 +117,10 @@ export class WebXRInputSource {
                                 this.motionController.rootMesh.parent = this.grip || this.pointer;
                                 this.motionController.disableAnimation = !!this._options.disableMotionControllerAnimation;
                             }
+                            // make sure to dispose is the controller is already disposed
+                            if (this._disposed) {
+                                this.motionController?.dispose();
+                            }
                         });
                     }
                 },
@@ -148,6 +153,7 @@ export class WebXRInputSource {
         this.onMeshLoadedObservable.clear();
         this.onDisposeObservable.notifyObservers(this);
         this.onDisposeObservable.clear();
+        this._disposed = true;
     }
 
     /**

+ 2 - 2
src/XR/webXRManagedOutputCanvas.ts

@@ -26,12 +26,12 @@ export class WebXRManagedOutputCanvasOptions {
      * Get the default values of the configuration object
      * @returns default values of this configuration object
      */
-    public static GetDefaults(): WebXRManagedOutputCanvasOptions {
+    public static GetDefaults(engine?: ThinEngine): WebXRManagedOutputCanvasOptions {
         const defaults = new WebXRManagedOutputCanvasOptions();
         defaults.canvasOptions = {
             antialias: true,
             depth: true,
-            stencil: false,
+            stencil: engine ? engine.isStencilEnable : true,
             alpha: true,
             multiview: false,
             framebufferScaleFactor: 1,

+ 1 - 1
src/XR/webXRSessionManager.ts

@@ -137,7 +137,7 @@ export class WebXRSessionManager implements IDisposable {
         if (this._xrNavigator.xr.native) {
             return this._xrNavigator.xr.getWebXRRenderTarget(engine);
         } else {
-            options = options || {};
+            options = options || WebXRManagedOutputCanvasOptions.GetDefaults(engine);
             options.canvasElement = engine.getRenderingCanvas() || undefined;
             return new WebXRManagedOutputCanvas(this, options);
         }