Kaynağa Gözat

Merge pull request #7523 from BabylonJS/master

Nightly
David Catuhe 5 yıl önce
ebeveyn
işleme
e5034b0a05
66 değiştirilmiş dosya ile 5585 ekleme ve 2574 silme
  1. 310 156
      dist/preview release/babylon.d.ts
  2. 2 2
      dist/preview release/babylon.js
  3. 1531 599
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 632 316
      dist/preview release/babylon.module.d.ts
  6. 310 156
      dist/preview release/documentation.d.ts
  7. 25 15
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  8. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.js.map
  9. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  10. 25 15
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  11. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  12. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  13. 25 15
      dist/preview release/loaders/babylon.glTFFileLoader.js
  14. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  15. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  16. 25 15
      dist/preview release/loaders/babylonjs.loaders.js
  17. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  18. 2 2
      dist/preview release/loaders/babylonjs.loaders.min.js
  19. 28 28
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  20. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  21. 1 1
      dist/preview release/packagesSizeBaseLine.json
  22. 632 316
      dist/preview release/viewer/babylon.module.d.ts
  23. 269 265
      dist/preview release/viewer/babylon.viewer.js
  24. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  25. 4 1
      dist/preview release/what's new.md
  26. 26 15
      loaders/src/glTF/glTFFileLoader.ts
  27. 36 38
      src/Cameras/XR/features/WebXRControllerPointerSelection.ts
  28. 129 104
      src/Cameras/XR/features/WebXRControllerTeleportation.ts
  29. 2 1
      src/Cameras/XR/motionController/index.ts
  30. 123 86
      src/Cameras/XR/motionController/webXRAbstractController.ts
  31. 22 4
      src/Cameras/XR/motionController/webXRControllerComponent.ts
  32. 54 23
      src/Cameras/XR/motionController/webXRGenericMotionController.ts
  33. 168 69
      src/Cameras/XR/motionController/webXRHTCViveMotionController.ts
  34. 402 75
      src/Cameras/XR/motionController/webXRMicrosoftMixedRealityController.ts
  35. 124 28
      src/Cameras/XR/motionController/webXRMotionControllerManager.ts
  36. 291 122
      src/Cameras/XR/motionController/webXROculusTouchMotionController.ts
  37. 143 0
      src/Cameras/XR/motionController/webXRProfiledMotionController.ts
  38. 3 1
      src/Cameras/XR/webXRCamera.ts
  39. 29 4
      src/Cameras/XR/webXRController.ts
  40. 35 4
      src/Cameras/XR/webXRInput.ts
  41. 2 0
      src/Cameras/arcRotateCamera.ts
  42. 11 0
      src/Cameras/camera.ts
  43. 2 0
      src/Cameras/targetCamera.ts
  44. 11 11
      src/Engines/Extensions/engine.cubeTexture.ts
  45. 2 4
      src/Engines/nativeEngine.ts
  46. 7 5
      src/Engines/thinEngine.ts
  47. 26 1
      src/Lights/Shadows/shadowGenerator.ts
  48. 6 0
      src/Materials/Node/Blocks/Input/inputBlock.ts
  49. 3 3
      src/Materials/Textures/Loaders/basisTextureLoader.ts
  50. 2 2
      src/Materials/Textures/Loaders/ddsTextureLoader.ts
  51. 2 3
      src/Materials/Textures/Loaders/envTextureLoader.ts
  52. 2 2
      src/Materials/Textures/Loaders/ktxTextureLoader.ts
  53. 5 5
      src/Materials/Textures/Loaders/tgaTextureLoader.ts
  54. 2 2
      src/Materials/Textures/internalTextureLoader.ts
  55. 2 2
      src/Meshes/Compression/dracoCompression.ts
  56. 9 4
      src/Misc/basis.ts
  57. 18 18
      src/Misc/dds.ts
  58. 8 8
      src/Misc/environmentTextureTools.ts
  59. 6 6
      src/Misc/khronosTextureContainer.ts
  60. 6 5
      src/Misc/tools.ts
  61. 7 3
      src/Rendering/utilityLayerRenderer.ts
  62. 4 0
      src/Shaders/shadowMap.fragment.fx
  63. 5 0
      src/Shaders/shadowMap.vertex.fx
  64. 16 1
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts
  65. BIN
      tests/validation/ReferenceImages/node-material0.png
  66. 1 1
      tests/validation/config.json

+ 310 - 156
dist/preview release/babylon.d.ts

@@ -1083,14 +1083,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed?: boolean) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed?: boolean) => void): void;
     }
 }
 declare module BABYLON {
@@ -7635,9 +7635,9 @@ declare module BABYLON {
              */
             createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>, format: number | undefined, forcedExtension: any, createPolynomials: boolean, lodScale: number, lodOffset: number): InternalTexture;
             /** @hidden */
-            _partialLoadFile(url: string, index: number, loadedFiles: (string | ArrayBuffer)[], onfinish: (files: (string | ArrayBuffer)[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void>): void;
+            _partialLoadFile(url: string, index: number, loadedFiles: ArrayBuffer[], onfinish: (files: ArrayBuffer[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void>): void;
             /** @hidden */
-            _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>): void;
+            _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: ArrayBuffer[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>): void;
             /** @hidden */
             _cascadeLoadImgs(scene: Nullable<Scene>, onfinish: (images: HTMLImageElement[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>, mimeType?: string): void;
             /** @hidden */
@@ -9295,6 +9295,20 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
+    export var clipPlaneFragmentDeclaration: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
+    export var clipPlaneFragment: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
     export var shadowMapPixelShader: {
         name: string;
         shader: string;
@@ -9337,6 +9351,13 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
+    export var clipPlaneVertexDeclaration: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
     export var morphTargetsVertex: {
         name: string;
         shader: string;
@@ -9358,6 +9379,13 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
+    export var clipPlaneVertex: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
     export var shadowMapVertexShader: {
         name: string;
         shader: string;
@@ -17307,13 +17335,6 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
-    export var clipPlaneFragmentDeclaration: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
     export var imageProcessingDeclaration: {
         name: string;
         shader: string;
@@ -17328,13 +17349,6 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
-    export var clipPlaneFragment: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
     export var particlesPixelShader: {
         name: string;
         shader: string;
@@ -17342,20 +17356,6 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
-    export var clipPlaneVertexDeclaration: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
-    export var clipPlaneVertex: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
     export var particlesVertexShader: {
         name: string;
         shader: string;
@@ -25457,6 +25457,15 @@ declare module BABYLON {
          * Observable triggered when reset has been called and applied to the camera.
          */
         onRestoreStateObservable: Observable<Camera>;
+        /**
+         * Is this camera a part of a rig system?
+         */
+        isRigCamera: boolean;
+        /**
+         * If isRigCamera set to true this will be set with the parent camera.
+         * The parent camera is not (!) necessarily the .parent of this camera (like in the case of XR)
+         */
+        rigParent?: Camera;
         /** @hidden */
         _cameraRigParams: any;
         /** @hidden */
@@ -34031,9 +34040,10 @@ declare module BABYLON {
         /**
          * Loads a file from a url
          * @param url the file url to load
-         * @returns a promise containing an ArrayBuffer corrisponding to the loaded file
+         * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
+         * @returns a promise containing an ArrayBuffer corresponding to the loaded file
          */
-        static LoadFileAsync(url: string): Promise<ArrayBuffer>;
+        static LoadFileAsync(url: string, useArrayBuffer?: boolean): Promise<ArrayBuffer | string>;
         /**
          * Load a script (identified by an url). When the url returns, the
          * content of this file is added into a new script element, attached to the DOM (body element)
@@ -42953,19 +42963,23 @@ declare module BABYLON {
         /**
          * Thumbstick component type
          */
-        static THUMBSTICK: string;
+        static THUMBSTICK: MotionControllerComponentType;
         /**
          * Touchpad component type
          */
-        static TOUCHPAD: string;
+        static TOUCHPAD: MotionControllerComponentType;
         /**
          * trigger component type
          */
-        static TRIGGER: string;
+        static TRIGGER: MotionControllerComponentType;
         /**
          * squeeze component type
          */
-        static SQUEEZE: string;
+        static SQUEEZE: MotionControllerComponentType;
+        /**
+         * button component type
+         */
+        static BUTTON: MotionControllerComponentType;
         /**
          * Observers registered here will be triggered when the state of a button changes
          * State change is either pressed / touched / value
@@ -42984,6 +42998,11 @@ declare module BABYLON {
         private _pressed;
         private _axes;
         private _changes;
+        private _hasChanges;
+        /**
+         * Return whether or not the component changed the last frame
+         */
+        get hasChanges(): boolean;
         /**
          * Creates a new component for a motion controller.
          * It is created by the motion controller itself
@@ -43457,13 +43476,17 @@ declare module BABYLON {
     /**
      * Handness type in xrInput profiles. These can be used to define layouts in the Layout Map.
      */
-    export type MotionControllerHandness = "none" | "left" | "right" | "left-right" | "left-right-none";
+    export type MotionControllerHandness = "none" | "left" | "right";
     /**
      * The type of components available in motion controllers.
      * This is not the name of the component.
      */
     export type MotionControllerComponentType = "trigger" | "squeeze" | "touchpad" | "thumbstick" | "button";
     /**
+     * The state of a controller component
+     */
+    export type MotionControllerComponentStateType = "default" | "touched" | "pressed";
+    /**
      * The schema of motion controller layout.
      * No object will be initialized using this interface
      * This is used just to define the profile.
@@ -43485,38 +43508,76 @@ declare module BABYLON {
                  * The type of input the component outputs
                  */
                 type: MotionControllerComponentType;
-            };
-        };
-        /**
-         * An optional gamepad object. If no gamepad object is not defined, no models will be loaded
-         */
-        gamepad?: {
-            /**
-             * Is the mapping based on the xr-standard defined here:
-             * https://www.w3.org/TR/webxr-gamepads-module-1/#xr-standard-gamepad-mapping
-             */
-            mapping: "" | "xr-standard";
-            /**
-             * The buttons available in this input in the right order
-             * index of this button will be the index in the gamepadObject.buttons array
-             * correlates to the componentId in components
-             */
-            buttons: Array<string | null>;
-            /**
-             * Definition of the axes of the gamepad input, sorted
-             * Correlates to componentIds in the components map
-             */
-            axes: Array<{
                 /**
-                 * The component id that the axis correlates to
+                 * The indices of this component in the gamepad object
+                 */
+                gamepadIndices: {
+                    /**
+                     * Index of button
+                     */
+                    button?: number;
+                    /**
+                     * If available, index of x-axis
+                     */
+                    xAxis?: number;
+                    /**
+                     * If available, index of y-axis
+                     */
+                    yAxis?: number;
+                };
+                /**
+                 * The mesh's root node name
                  */
-                componentId: string;
+                rootNodeName: string;
                 /**
-                 * X or Y Axis
+                 * Animation definitions for this model
                  */
-                axis: "x-axis" | "y-axis";
-            } | null>;
+                visualResponses: {
+                    [stateKey: string]: {
+                        /**
+                         * What property will be animated
+                         */
+                        componentProperty: "xAxis" | "yAxis" | "button" | "state";
+                        /**
+                         * What states influence this visual reponse
+                         */
+                        states: MotionControllerComponentStateType[];
+                        /**
+                         * Type of animation - movement or visibility
+                         */
+                        valueNodeProperty: "transform" | "visibility";
+                        /**
+                         * Base node name to move. Its position will be calculated according to the min and max nodes
+                         */
+                        valueNodeName?: string;
+                        /**
+                         * Minimum movement node
+                         */
+                        minNodeName?: string;
+                        /**
+                         * Max movement node
+                         */
+                        maxNodeName?: string;
+                    };
+                };
+                /**
+                 * If touch enabled, what is the name of node to display user feedback
+                 */
+                touchPointNodeName?: string;
+            };
         };
+        /**
+         * Is it xr standard mapping or not
+         */
+        gamepadMapping: "" | "xr-standard";
+        /**
+         * Base root node of this entire model
+         */
+        rootNodeName: string;
+        /**
+         * Path to load the assets. Usually relative to the base path
+         */
+        assetPath: string;
     }
     /**
      * A definition for the layout map in the input profile
@@ -43572,7 +43633,7 @@ declare module BABYLON {
      * This will be expanded when touchpad animations are fully supported
      * The meshes are provided to the _lerpAxisTransform function to calculate the current position of the value mesh
      */
-    export interface IMotionControllerAxisMeshMap {
+    export interface IMotionControllerMeshMap {
         /**
          * The mesh that will be changed when axis value changes
          */
@@ -43580,11 +43641,11 @@ declare module BABYLON {
         /**
          * the mesh that defines the minimum value mesh position.
          */
-        minMesh: AbstractMesh;
+        minMesh?: AbstractMesh;
         /**
          * the mesh that defines the maximum value mesh position.
          */
-        maxMesh: AbstractMesh;
+        maxMesh?: AbstractMesh;
     }
     /**
      * The elements needed for change-detection of the gamepad objects in motion controllers
@@ -43629,16 +43690,6 @@ declare module BABYLON {
          */
         handness: MotionControllerHandness;
         /**
-         * Component type map
-         */
-        static ComponentType: {
-            TRIGGER: string;
-            SQUEEZE: string;
-            TOUCHPAD: string;
-            THUMBSTICK: string;
-            BUTTON: string;
-        };
-        /**
          * The profile id of this motion controller
          */
         abstract profileId: string;
@@ -43657,6 +43708,10 @@ declare module BABYLON {
          * The root mesh of the model. It is null if the model was not yet initialized
          */
         rootMesh: Nullable<AbstractMesh>;
+        /**
+         * Disable the model's animation. Can be set at any time.
+         */
+        disableAnimation: boolean;
         private _modelReady;
         /**
          * constructs a new abstract motion controller
@@ -43685,7 +43740,7 @@ declare module BABYLON {
          * Get the list of components available in this motion controller
          * @returns an array of strings correlating to available components
          */
-        getComponentTypes(): string[];
+        getComponentIds(): string[];
         /**
          * Get the main (Select) component of this controller as defined in the layout
          * @returns the main component of this controller
@@ -43698,6 +43753,18 @@ declare module BABYLON {
          */
         getComponent(id: string): WebXRControllerComponent;
         /**
+         * Get the first component of specific type
+         * @param type type of component to find
+         * @return a controller component or null if not found
+         */
+        getComponentOfType(type: MotionControllerComponentType): Nullable<WebXRControllerComponent>;
+        /**
+         * Returns all components of specific type
+         * @param type the type to search for
+         * @return an array of components with this type
+         */
+        getAllComponentsOfType(type: MotionControllerComponentType): WebXRControllerComponent[];
+        /**
          * Loads the model correlating to this controller
          * When the mesh is loaded, the onModelLoadedObservable will be triggered
          * @returns A promise fulfilled with the result of the model loading
@@ -43714,13 +43781,9 @@ declare module BABYLON {
          * @param axisValue the value of the axis which determines the meshes new position
          * @hidden
          */
-        protected _lerpAxisTransform(axisMap: IMotionControllerAxisMeshMap, axisValue: number): void;
-        /**
-         * Moves the buttons on the controller mesh based on their current state
-         * @param buttonName the name of the button to move
-         * @param buttonValue the value of the button which determines the buttons new position
-         */
-        protected _lerpButtonTransform(buttonMap: IMotionControllerButtonMeshMap, buttonValue: number): void;
+        protected _lerpTransform(axisMap: IMotionControllerMeshMap, axisValue: number, fixValueCoordinates?: boolean): void;
+        protected _getChildByName(node: AbstractMesh, name: string): AbstractMesh;
+        protected _getImmediateChildByName(node: AbstractMesh, name: string): AbstractMesh;
         private _getGenericFilenameAndPath;
         private _getGenericParentMesh;
         /**
@@ -43783,6 +43846,67 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Class containing static functions to help procedurally build meshes
+     */
+    export class SphereBuilder {
+        /**
+         * Creates a sphere mesh
+         * * The parameter `diameter` sets the diameter size (float) of the sphere (default 1)
+         * * You can set some different sphere dimensions, for instance to build an ellipsoid, by using the parameters `diameterX`, `diameterY` and `diameterZ` (all by default have the same value of `diameter`)
+         * * The parameter `segments` sets the sphere number of horizontal stripes (positive integer, default 32)
+         * * You can create an unclosed sphere with the parameter `arc` (positive float, default 1), valued between 0 and 1, what is the ratio of the circumference (latitude) : 2 x PI x ratio
+         * * You can create an unclosed sphere on its height with the parameter `slice` (positive float, default1), valued between 0 and 1, what is the height ratio (longitude)
+         * * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
+         * * If you create a double-sided mesh, you can choose what parts of the texture image to crop and stick respectively on the front and the back sides with the parameters `frontUVs` and `backUVs` (Vector4). Detail here : https://doc.babylonjs.com/babylon101/discover_basic_elements#side-orientation
+         * * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created
+         * @param name defines the name of the mesh
+         * @param options defines the options used to create the mesh
+         * @param scene defines the hosting scene
+         * @returns the sphere mesh
+         * @see https://doc.babylonjs.com/how_to/set_shapes#sphere
+         */
+        static CreateSphere(name: string, options: {
+            segments?: number;
+            diameter?: number;
+            diameterX?: number;
+            diameterY?: number;
+            diameterZ?: number;
+            arc?: number;
+            slice?: number;
+            sideOrientation?: number;
+            frontUVs?: Vector4;
+            backUVs?: Vector4;
+            updatable?: boolean;
+        }, scene?: Nullable<Scene>): Mesh;
+    }
+}
+declare module BABYLON {
+    /**
+     * A profiled motion controller has its profile loaded from an online repository.
+     * The class is responsible of loading the model, mapping the keys and enabling model-animations
+     */
+    export class WebXRProfiledMotionController extends WebXRAbstractMotionController {
+        private _repositoryUrl;
+        /**
+         * The profile ID of this controller. Will be populated when the controller initializes.
+         */
+        profileId: string;
+        private _buttonMeshMapping;
+        constructor(scene: Scene, xrInput: XRInputSource, _profile: IMotionControllerProfile, _repositoryUrl: string);
+        protected _getFilenameAndPath(): {
+            filename: string;
+            path: string;
+        };
+        private _touchDots;
+        protected _processLoadedModel(_meshes: AbstractMesh[]): void;
+        protected _setRootMesh(meshes: AbstractMesh[]): void;
+        protected _updateModel(_xrFrame: XRFrame): void;
+        protected _getModelLoadingConstraints(): boolean;
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
      * A construction function type to create a new controller based on an xrInput object
      */
     export type MotionControllerConstructor = (xrInput: XRInputSource, scene: Scene) => WebXRAbstractMotionController;
@@ -43795,6 +43919,18 @@ declare module BABYLON {
      * When using a model try to stay as generic as possible. Eventually there will be no need in any of the controller classes
      */
     export class WebXRMotionControllerManager {
+        /**
+         * The base URL of the online controller repository. Can be changed at any time.
+         */
+        static BaseRepositoryUrl: string;
+        /**
+         * Use the online repository, or use only locally-defined controllers
+         */
+        static UseOnlineRepository: boolean;
+        /**
+         * Which repository gets priority - local or online
+         */
+        static PrioritizeOnlineRepository: boolean;
         private static _AvailableControllers;
         private static _Fallbacks;
         /**
@@ -43818,9 +43954,24 @@ declare module BABYLON {
          * @param xrInput the xrInput to which a new controller is initialized
          * @param scene the scene to which the model will be added
          * @param forceProfile force a certain profile for this controller
-         * @return the motion controller class for this profile id or the generic standard class if none was found
+         * @return A promise that fulfils with the motion controller class for this profile id or the generic standard class if none was found
+         */
+        static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene, forceProfile?: string): Promise<WebXRAbstractMotionController>;
+        private static _LoadProfilesFromAvailableControllers;
+        private static _ProfilesList;
+        private static _ProfileLoadingPromises;
+        private static _LoadProfileFromRepository;
+        /**
+         * Clear the cache used for profile loading and reload when requested again
+         */
+        static ClearProfilesCache(): void;
+        /**
+         * Will update the list of profiles available in the repository
+         * @return a promise that resolves to a map of profiles available online
          */
-        static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene, forceProfile?: string): WebXRAbstractMotionController;
+        static UpdateProfilesList(): Promise<{
+            [profile: string]: string;
+        }>;
         /**
          * Find a fallback profile if the profile was not found. There are a few predefined generic profiles.
          * @param profileId the profile to which a fallback needs to be found
@@ -43850,6 +44001,15 @@ declare module BABYLON {
          * This can be used when creating your own profile or when testing different controllers
          */
         forceControllerProfile?: string;
+        /**
+         * Do not load the controller mesh, in case a different mesh needs to be loaded.
+         */
+        doNotLoadControllerMesh?: boolean;
+        /**
+         * Should the controller mesh be animated when a user interacts with it
+         * The pressed buttons / thumbstick and touchpad animations will be disabled
+         */
+        disableMotionControllerAnimation?: boolean;
     }
     /**
      * Represents an XR controller
@@ -43874,6 +44034,10 @@ declare module BABYLON {
          */
         motionController?: WebXRAbstractMotionController;
         /**
+         * Observers registered here will trigger when a motion controller profile was assigned to this xr controller
+         */
+        onMotionControllerProfileLoaded: Observable<WebXRAbstractMotionController>;
+        /**
          * Event that fires when the controller is removed/disposed
          */
         onDisposeObservable: Observable<{}>;
@@ -43926,6 +44090,20 @@ declare module BABYLON {
          * Profiles are defined here - https://github.com/immersive-web/webxr-input-profiles/
          */
         forceInputProfile?: string;
+        /**
+         * Do not send a request to the controlle repository to load the profile.
+         *
+         * Instead, use the controllers available in babylon itself.
+         */
+        disableOnlineControllerRepository?: boolean;
+        /**
+         * A custom URL for the controllers repository
+         */
+        customControllersRepositoryURL?: string;
+        /**
+         * Should the controller model's components not move according to the user input
+         */
+        disableControllerAnimation?: boolean;
     }
     /**
      * XR input used to track XR inputs such as controllers/rays
@@ -44369,13 +44547,17 @@ declare module BABYLON {
              */
             teleportationBorderColor?: string;
             /**
-             * Override the default material of the torus and arrow
-             */
-            torusArrowMaterial?: Material;
-            /**
              * Disable the mesh's animation sequence
              */
             disableAnimation?: boolean;
+            /**
+             * Disable lighting on the material or the ring and arrow
+             */
+            disableLighting?: boolean;
+            /**
+             * Override the default material of the torus and arrow
+             */
+            torusArrowMaterial?: Material;
         };
         /**
          * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
@@ -44426,6 +44608,10 @@ declare module BABYLON {
          */
         rotationAngle: number;
         /**
+         * Is movement backwards enabled
+         */
+        backwardsMovementEnabled: boolean;
+        /**
          * Distance to travel when moving backwards
          */
         backwardsTeleportationDistance: number;
@@ -45455,8 +45641,6 @@ declare module BABYLON {
         };
         constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness);
         protected _processLoadedModel(_meshes: AbstractMesh[]): void;
-        private _getChildByName;
-        private _getImmediateChildByName;
         protected _getFilenameAndPath(): {
             filename: string;
             path: string;
@@ -45522,7 +45706,13 @@ declare module BABYLON {
         static MODEL_FILENAME: string;
         profileId: string;
         private _modelRootNode;
-        constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness, legacyMapping?: boolean);
+        /**
+         * Create a new Vive motion controller object
+         * @param scene the scene to use to create this controller
+         * @param gamepadObject the corresponding gamepad object
+         * @param handness the handness of the controller
+         */
+        constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness);
         protected _processLoadedModel(_meshes: AbstractMesh[]): void;
         protected _getFilenameAndPath(): {
             filename: string;
@@ -45826,7 +46016,7 @@ declare module BABYLON {
          * Gets the camera that is used to render the utility layer (when not set, this will be the last active camera)
          * @returns the camera that is used when rendering the utility layer
          */
-        getRenderCamera(): Nullable<Camera>;
+        getRenderCamera(): Camera;
         /**
          * Sets the camera that should be used when rendering the utility layer (If set to null the last active camera will be used)
          * @param cam the camera that should be used when rendering the utility layer
@@ -46430,42 +46620,6 @@ declare module BABYLON {
         }, scene?: Nullable<Scene>): Mesh;
     }
 }
-declare module BABYLON {
-    /**
-     * Class containing static functions to help procedurally build meshes
-     */
-    export class SphereBuilder {
-        /**
-         * Creates a sphere mesh
-         * * The parameter `diameter` sets the diameter size (float) of the sphere (default 1)
-         * * You can set some different sphere dimensions, for instance to build an ellipsoid, by using the parameters `diameterX`, `diameterY` and `diameterZ` (all by default have the same value of `diameter`)
-         * * The parameter `segments` sets the sphere number of horizontal stripes (positive integer, default 32)
-         * * You can create an unclosed sphere with the parameter `arc` (positive float, default 1), valued between 0 and 1, what is the ratio of the circumference (latitude) : 2 x PI x ratio
-         * * You can create an unclosed sphere on its height with the parameter `slice` (positive float, default1), valued between 0 and 1, what is the height ratio (longitude)
-         * * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
-         * * If you create a double-sided mesh, you can choose what parts of the texture image to crop and stick respectively on the front and the back sides with the parameters `frontUVs` and `backUVs` (Vector4). Detail here : https://doc.babylonjs.com/babylon101/discover_basic_elements#side-orientation
-         * * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created
-         * @param name defines the name of the mesh
-         * @param options defines the options used to create the mesh
-         * @param scene defines the hosting scene
-         * @returns the sphere mesh
-         * @see https://doc.babylonjs.com/how_to/set_shapes#sphere
-         */
-        static CreateSphere(name: string, options: {
-            segments?: number;
-            diameter?: number;
-            diameterX?: number;
-            diameterY?: number;
-            diameterZ?: number;
-            arc?: number;
-            slice?: number;
-            sideOrientation?: number;
-            frontUVs?: Vector4;
-            backUVs?: Vector4;
-            updatable?: boolean;
-        }, scene?: Nullable<Scene>): Mesh;
-    }
-}
 declare module BABYLON.Debug {
     /**
          * Used to show the physics impostor around the specific mesh
@@ -47606,7 +47760,7 @@ declare module BABYLON {
          * @param data The array buffer containing the .env bytes.
          * @returns the environment file info (the json header) if successfully parsed.
          */
-        static GetEnvInfo(data: ArrayBuffer): Nullable<EnvironmentTextureInfo>;
+        static GetEnvInfo(data: ArrayBufferView): Nullable<EnvironmentTextureInfo>;
         /**
          * Creates an environment texture from a loaded cube texture.
          * @param texture defines the cube texture to convert in env file
@@ -47621,19 +47775,19 @@ declare module BABYLON {
         private static _CreateEnvTextureIrradiance;
         /**
          * Creates the ArrayBufferViews used for initializing environment texture image data.
-         * @param arrayBuffer the underlying ArrayBuffer to which the views refer
+         * @param data the image data
          * @param info parameters that determine what views will be created for accessing the underlying buffer
          * @return the views described by info providing access to the underlying buffer
          */
-        static CreateImageDataArrayBufferViews(arrayBuffer: any, info: EnvironmentTextureInfo): Array<Array<ArrayBufferView>>;
+        static CreateImageDataArrayBufferViews(data: ArrayBufferView, info: EnvironmentTextureInfo): Array<Array<ArrayBufferView>>;
         /**
          * Uploads the texture info contained in the env file to the GPU.
          * @param texture defines the internal texture to upload to
-         * @param arrayBuffer defines the buffer cotaining the data to load
+         * @param data defines the data to load
          * @param info defines the texture info retrieved through the GetEnvInfo method
          * @returns a promise
          */
-        static UploadEnvLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void>;
+        static UploadEnvLevelsAsync(texture: InternalTexture, data: ArrayBufferView, info: EnvironmentTextureInfo): Promise<void>;
         private static _OnImageReadyAsync;
         /**
          * Uploads the levels of image data to the GPU.
@@ -52164,10 +52318,10 @@ declare module BABYLON {
         static StoreLODInAlphaChannel: boolean;
         /**
          * Gets DDS information from an array buffer
-         * @param arrayBuffer defines the array buffer to read data from
+         * @param data defines the array buffer view to read data from
          * @returns the DDS information
          */
-        static GetDDSInfo(arrayBuffer: any): DDSInfo;
+        static GetDDSInfo(data: ArrayBufferView): DDSInfo;
         private static _FloatView;
         private static _Int32View;
         private static _ToHalfFloat;
@@ -52185,7 +52339,7 @@ declare module BABYLON {
          * Uploads DDS Levels to a Babylon Texture
          * @hidden
          */
-        static UploadDDSLevels(engine: ThinEngine, texture: InternalTexture, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex?: number, currentFace?: number): void;
+        static UploadDDSLevels(engine: ThinEngine, texture: InternalTexture, data: ArrayBufferView, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex?: number, currentFace?: number): void;
     }
         interface ThinEngine {
             /**
@@ -52246,14 +52400,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(imgs: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(imgs: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {
@@ -52298,14 +52452,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {
@@ -52315,7 +52469,7 @@ declare module BABYLON {
      */
     export class KhronosTextureContainer {
         /** contents of the KTX container file */
-        arrayBuffer: any;
+        data: ArrayBufferView;
         private static HEADER_LEN;
         private static COMPRESSED_2D;
         private static COMPRESSED_3D;
@@ -52379,14 +52533,14 @@ declare module BABYLON {
         isInvalid: boolean;
         /**
          * Creates a new KhronosTextureContainer
-         * @param arrayBuffer contents of the KTX container file
+         * @param data contents of the KTX container file
          * @param facesExpected should be either 1 or 6, based whether a cube texture or or
          * @param threeDExpected provision for indicating that data should be a 3D texture, not implemented
          * @param textureArrayExpected provision for indicating that data should be a texture array, not implemented
          */
         constructor(
         /** contents of the KTX container file */
-        arrayBuffer: any, facesExpected: number, threeDExpected?: boolean, textureArrayExpected?: boolean);
+        data: ArrayBufferView, facesExpected: number, threeDExpected?: boolean, textureArrayExpected?: boolean);
         /**
          * Uploads KTX content to a Babylon Texture.
          * It is assumed that the texture has already been created & is currently bound
@@ -52438,14 +52592,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void;
     }
 }
 declare module BABYLON {
@@ -56196,14 +56350,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {
@@ -56299,11 +56453,11 @@ declare module BABYLON {
         private static _CreateWorkerAsync;
         /**
          * Transcodes a loaded image file to compressed pixel data
-         * @param imageData image data to transcode
+         * @param data image data to transcode
          * @param config configuration options for the transcoding
          * @returns a promise resulting in the transcoded image
          */
-        static TranscodeAsync(imageData: ArrayBuffer, config: BasisTranscodeConfiguration): Promise<TranscodeResult>;
+        static TranscodeAsync(data: ArrayBuffer | ArrayBufferView, config: BasisTranscodeConfiguration): Promise<TranscodeResult>;
         /**
          * Loads a texture from the transcode result
          * @param texture texture load to
@@ -56353,14 +56507,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2 - 2
dist/preview release/babylon.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1531 - 599
dist/preview release/babylon.max.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/babylon.max.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 632 - 316
dist/preview release/babylon.module.d.ts


+ 310 - 156
dist/preview release/documentation.d.ts

@@ -1083,14 +1083,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed?: boolean) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed?: boolean) => void): void;
     }
 }
 declare module BABYLON {
@@ -7635,9 +7635,9 @@ declare module BABYLON {
              */
             createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>, format: number | undefined, forcedExtension: any, createPolynomials: boolean, lodScale: number, lodOffset: number): InternalTexture;
             /** @hidden */
-            _partialLoadFile(url: string, index: number, loadedFiles: (string | ArrayBuffer)[], onfinish: (files: (string | ArrayBuffer)[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void>): void;
+            _partialLoadFile(url: string, index: number, loadedFiles: ArrayBuffer[], onfinish: (files: ArrayBuffer[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void>): void;
             /** @hidden */
-            _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>): void;
+            _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: ArrayBuffer[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>): void;
             /** @hidden */
             _cascadeLoadImgs(scene: Nullable<Scene>, onfinish: (images: HTMLImageElement[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>, mimeType?: string): void;
             /** @hidden */
@@ -9295,6 +9295,20 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
+    export var clipPlaneFragmentDeclaration: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
+    export var clipPlaneFragment: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
     export var shadowMapPixelShader: {
         name: string;
         shader: string;
@@ -9337,6 +9351,13 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
+    export var clipPlaneVertexDeclaration: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
     export var morphTargetsVertex: {
         name: string;
         shader: string;
@@ -9358,6 +9379,13 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
+    export var clipPlaneVertex: {
+        name: string;
+        shader: string;
+    };
+}
+declare module BABYLON {
+    /** @hidden */
     export var shadowMapVertexShader: {
         name: string;
         shader: string;
@@ -17307,13 +17335,6 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
-    export var clipPlaneFragmentDeclaration: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
     export var imageProcessingDeclaration: {
         name: string;
         shader: string;
@@ -17328,13 +17349,6 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
-    export var clipPlaneFragment: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
     export var particlesPixelShader: {
         name: string;
         shader: string;
@@ -17342,20 +17356,6 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @hidden */
-    export var clipPlaneVertexDeclaration: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
-    export var clipPlaneVertex: {
-        name: string;
-        shader: string;
-    };
-}
-declare module BABYLON {
-    /** @hidden */
     export var particlesVertexShader: {
         name: string;
         shader: string;
@@ -25457,6 +25457,15 @@ declare module BABYLON {
          * Observable triggered when reset has been called and applied to the camera.
          */
         onRestoreStateObservable: Observable<Camera>;
+        /**
+         * Is this camera a part of a rig system?
+         */
+        isRigCamera: boolean;
+        /**
+         * If isRigCamera set to true this will be set with the parent camera.
+         * The parent camera is not (!) necessarily the .parent of this camera (like in the case of XR)
+         */
+        rigParent?: Camera;
         /** @hidden */
         _cameraRigParams: any;
         /** @hidden */
@@ -34031,9 +34040,10 @@ declare module BABYLON {
         /**
          * Loads a file from a url
          * @param url the file url to load
-         * @returns a promise containing an ArrayBuffer corrisponding to the loaded file
+         * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
+         * @returns a promise containing an ArrayBuffer corresponding to the loaded file
          */
-        static LoadFileAsync(url: string): Promise<ArrayBuffer>;
+        static LoadFileAsync(url: string, useArrayBuffer?: boolean): Promise<ArrayBuffer | string>;
         /**
          * Load a script (identified by an url). When the url returns, the
          * content of this file is added into a new script element, attached to the DOM (body element)
@@ -42953,19 +42963,23 @@ declare module BABYLON {
         /**
          * Thumbstick component type
          */
-        static THUMBSTICK: string;
+        static THUMBSTICK: MotionControllerComponentType;
         /**
          * Touchpad component type
          */
-        static TOUCHPAD: string;
+        static TOUCHPAD: MotionControllerComponentType;
         /**
          * trigger component type
          */
-        static TRIGGER: string;
+        static TRIGGER: MotionControllerComponentType;
         /**
          * squeeze component type
          */
-        static SQUEEZE: string;
+        static SQUEEZE: MotionControllerComponentType;
+        /**
+         * button component type
+         */
+        static BUTTON: MotionControllerComponentType;
         /**
          * Observers registered here will be triggered when the state of a button changes
          * State change is either pressed / touched / value
@@ -42984,6 +42998,11 @@ declare module BABYLON {
         private _pressed;
         private _axes;
         private _changes;
+        private _hasChanges;
+        /**
+         * Return whether or not the component changed the last frame
+         */
+        get hasChanges(): boolean;
         /**
          * Creates a new component for a motion controller.
          * It is created by the motion controller itself
@@ -43457,13 +43476,17 @@ declare module BABYLON {
     /**
      * Handness type in xrInput profiles. These can be used to define layouts in the Layout Map.
      */
-    export type MotionControllerHandness = "none" | "left" | "right" | "left-right" | "left-right-none";
+    export type MotionControllerHandness = "none" | "left" | "right";
     /**
      * The type of components available in motion controllers.
      * This is not the name of the component.
      */
     export type MotionControllerComponentType = "trigger" | "squeeze" | "touchpad" | "thumbstick" | "button";
     /**
+     * The state of a controller component
+     */
+    export type MotionControllerComponentStateType = "default" | "touched" | "pressed";
+    /**
      * The schema of motion controller layout.
      * No object will be initialized using this interface
      * This is used just to define the profile.
@@ -43485,38 +43508,76 @@ declare module BABYLON {
                  * The type of input the component outputs
                  */
                 type: MotionControllerComponentType;
-            };
-        };
-        /**
-         * An optional gamepad object. If no gamepad object is not defined, no models will be loaded
-         */
-        gamepad?: {
-            /**
-             * Is the mapping based on the xr-standard defined here:
-             * https://www.w3.org/TR/webxr-gamepads-module-1/#xr-standard-gamepad-mapping
-             */
-            mapping: "" | "xr-standard";
-            /**
-             * The buttons available in this input in the right order
-             * index of this button will be the index in the gamepadObject.buttons array
-             * correlates to the componentId in components
-             */
-            buttons: Array<string | null>;
-            /**
-             * Definition of the axes of the gamepad input, sorted
-             * Correlates to componentIds in the components map
-             */
-            axes: Array<{
                 /**
-                 * The component id that the axis correlates to
+                 * The indices of this component in the gamepad object
+                 */
+                gamepadIndices: {
+                    /**
+                     * Index of button
+                     */
+                    button?: number;
+                    /**
+                     * If available, index of x-axis
+                     */
+                    xAxis?: number;
+                    /**
+                     * If available, index of y-axis
+                     */
+                    yAxis?: number;
+                };
+                /**
+                 * The mesh's root node name
                  */
-                componentId: string;
+                rootNodeName: string;
                 /**
-                 * X or Y Axis
+                 * Animation definitions for this model
                  */
-                axis: "x-axis" | "y-axis";
-            } | null>;
+                visualResponses: {
+                    [stateKey: string]: {
+                        /**
+                         * What property will be animated
+                         */
+                        componentProperty: "xAxis" | "yAxis" | "button" | "state";
+                        /**
+                         * What states influence this visual reponse
+                         */
+                        states: MotionControllerComponentStateType[];
+                        /**
+                         * Type of animation - movement or visibility
+                         */
+                        valueNodeProperty: "transform" | "visibility";
+                        /**
+                         * Base node name to move. Its position will be calculated according to the min and max nodes
+                         */
+                        valueNodeName?: string;
+                        /**
+                         * Minimum movement node
+                         */
+                        minNodeName?: string;
+                        /**
+                         * Max movement node
+                         */
+                        maxNodeName?: string;
+                    };
+                };
+                /**
+                 * If touch enabled, what is the name of node to display user feedback
+                 */
+                touchPointNodeName?: string;
+            };
         };
+        /**
+         * Is it xr standard mapping or not
+         */
+        gamepadMapping: "" | "xr-standard";
+        /**
+         * Base root node of this entire model
+         */
+        rootNodeName: string;
+        /**
+         * Path to load the assets. Usually relative to the base path
+         */
+        assetPath: string;
     }
     /**
      * A definition for the layout map in the input profile
@@ -43572,7 +43633,7 @@ declare module BABYLON {
      * This will be expanded when touchpad animations are fully supported
      * The meshes are provided to the _lerpAxisTransform function to calculate the current position of the value mesh
      */
-    export interface IMotionControllerAxisMeshMap {
+    export interface IMotionControllerMeshMap {
         /**
          * The mesh that will be changed when axis value changes
          */
@@ -43580,11 +43641,11 @@ declare module BABYLON {
         /**
          * the mesh that defines the minimum value mesh position.
          */
-        minMesh: AbstractMesh;
+        minMesh?: AbstractMesh;
         /**
          * the mesh that defines the maximum value mesh position.
          */
-        maxMesh: AbstractMesh;
+        maxMesh?: AbstractMesh;
     }
     /**
      * The elements needed for change-detection of the gamepad objects in motion controllers
@@ -43629,16 +43690,6 @@ declare module BABYLON {
          */
         handness: MotionControllerHandness;
         /**
-         * Component type map
-         */
-        static ComponentType: {
-            TRIGGER: string;
-            SQUEEZE: string;
-            TOUCHPAD: string;
-            THUMBSTICK: string;
-            BUTTON: string;
-        };
-        /**
          * The profile id of this motion controller
          */
         abstract profileId: string;
@@ -43657,6 +43708,10 @@ declare module BABYLON {
          * The root mesh of the model. It is null if the model was not yet initialized
          */
         rootMesh: Nullable<AbstractMesh>;
+        /**
+         * Disable the model's animation. Can be set at any time.
+         */
+        disableAnimation: boolean;
         private _modelReady;
         /**
          * constructs a new abstract motion controller
@@ -43685,7 +43740,7 @@ declare module BABYLON {
          * Get the list of components available in this motion controller
          * @returns an array of strings correlating to available components
          */
-        getComponentTypes(): string[];
+        getComponentIds(): string[];
         /**
          * Get the main (Select) component of this controller as defined in the layout
          * @returns the main component of this controller
@@ -43698,6 +43753,18 @@ declare module BABYLON {
          */
         getComponent(id: string): WebXRControllerComponent;
         /**
+         * Get the first component of specific type
+         * @param type type of component to find
+         * @return a controller component or null if not found
+         */
+        getComponentOfType(type: MotionControllerComponentType): Nullable<WebXRControllerComponent>;
+        /**
+         * Returns all components of specific type
+         * @param type the type to search for
+         * @return an array of components with this type
+         */
+        getAllComponentsOfType(type: MotionControllerComponentType): WebXRControllerComponent[];
+        /**
          * Loads the model correlating to this controller
          * When the mesh is loaded, the onModelLoadedObservable will be triggered
          * @returns A promise fulfilled with the result of the model loading
@@ -43714,13 +43781,9 @@ declare module BABYLON {
          * @param axisValue the value of the axis which determines the meshes new position
          * @hidden
          */
-        protected _lerpAxisTransform(axisMap: IMotionControllerAxisMeshMap, axisValue: number): void;
-        /**
-         * Moves the buttons on the controller mesh based on their current state
-         * @param buttonName the name of the button to move
-         * @param buttonValue the value of the button which determines the buttons new position
-         */
-        protected _lerpButtonTransform(buttonMap: IMotionControllerButtonMeshMap, buttonValue: number): void;
+        protected _lerpTransform(axisMap: IMotionControllerMeshMap, axisValue: number, fixValueCoordinates?: boolean): void;
+        protected _getChildByName(node: AbstractMesh, name: string): AbstractMesh;
+        protected _getImmediateChildByName(node: AbstractMesh, name: string): AbstractMesh;
         private _getGenericFilenameAndPath;
         private _getGenericParentMesh;
         /**
@@ -43783,6 +43846,67 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Class containing static functions to help procedurally build meshes
+     */
+    export class SphereBuilder {
+        /**
+         * Creates a sphere mesh
+         * * The parameter `diameter` sets the diameter size (float) of the sphere (default 1)
+         * * You can set some different sphere dimensions, for instance to build an ellipsoid, by using the parameters `diameterX`, `diameterY` and `diameterZ` (all by default have the same value of `diameter`)
+         * * The parameter `segments` sets the sphere number of horizontal stripes (positive integer, default 32)
+         * * You can create an unclosed sphere with the parameter `arc` (positive float, default 1), valued between 0 and 1, what is the ratio of the circumference (latitude) : 2 x PI x ratio
+         * * You can create an unclosed sphere on its height with the parameter `slice` (positive float, default1), valued between 0 and 1, what is the height ratio (longitude)
+         * * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
+         * * If you create a double-sided mesh, you can choose what parts of the texture image to crop and stick respectively on the front and the back sides with the parameters `frontUVs` and `backUVs` (Vector4). Detail here : https://doc.babylonjs.com/babylon101/discover_basic_elements#side-orientation
+         * * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created
+         * @param name defines the name of the mesh
+         * @param options defines the options used to create the mesh
+         * @param scene defines the hosting scene
+         * @returns the sphere mesh
+         * @see https://doc.babylonjs.com/how_to/set_shapes#sphere
+         */
+        static CreateSphere(name: string, options: {
+            segments?: number;
+            diameter?: number;
+            diameterX?: number;
+            diameterY?: number;
+            diameterZ?: number;
+            arc?: number;
+            slice?: number;
+            sideOrientation?: number;
+            frontUVs?: Vector4;
+            backUVs?: Vector4;
+            updatable?: boolean;
+        }, scene?: Nullable<Scene>): Mesh;
+    }
+}
+declare module BABYLON {
+    /**
+     * A profiled motion controller has its profile loaded from an online repository.
+     * The class is responsible of loading the model, mapping the keys and enabling model-animations
+     */
+    export class WebXRProfiledMotionController extends WebXRAbstractMotionController {
+        private _repositoryUrl;
+        /**
+         * The profile ID of this controller. Will be populated when the controller initializes.
+         */
+        profileId: string;
+        private _buttonMeshMapping;
+        constructor(scene: Scene, xrInput: XRInputSource, _profile: IMotionControllerProfile, _repositoryUrl: string);
+        protected _getFilenameAndPath(): {
+            filename: string;
+            path: string;
+        };
+        private _touchDots;
+        protected _processLoadedModel(_meshes: AbstractMesh[]): void;
+        protected _setRootMesh(meshes: AbstractMesh[]): void;
+        protected _updateModel(_xrFrame: XRFrame): void;
+        protected _getModelLoadingConstraints(): boolean;
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
      * A construction function type to create a new controller based on an xrInput object
      */
     export type MotionControllerConstructor = (xrInput: XRInputSource, scene: Scene) => WebXRAbstractMotionController;
@@ -43795,6 +43919,18 @@ declare module BABYLON {
      * When using a model try to stay as generic as possible. Eventually there will be no need in any of the controller classes
      */
     export class WebXRMotionControllerManager {
+        /**
+         * The base URL of the online controller repository. Can be changed at any time.
+         */
+        static BaseRepositoryUrl: string;
+        /**
+         * Use the online repository, or use only locally-defined controllers
+         */
+        static UseOnlineRepository: boolean;
+        /**
+         * Which repository gets priority - local or online
+         */
+        static PrioritizeOnlineRepository: boolean;
         private static _AvailableControllers;
         private static _Fallbacks;
         /**
@@ -43818,9 +43954,24 @@ declare module BABYLON {
          * @param xrInput the xrInput to which a new controller is initialized
          * @param scene the scene to which the model will be added
          * @param forceProfile force a certain profile for this controller
-         * @return the motion controller class for this profile id or the generic standard class if none was found
+         * @return A promise that fulfils with the motion controller class for this profile id or the generic standard class if none was found
+         */
+        static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene, forceProfile?: string): Promise<WebXRAbstractMotionController>;
+        private static _LoadProfilesFromAvailableControllers;
+        private static _ProfilesList;
+        private static _ProfileLoadingPromises;
+        private static _LoadProfileFromRepository;
+        /**
+         * Clear the cache used for profile loading and reload when requested again
+         */
+        static ClearProfilesCache(): void;
+        /**
+         * Will update the list of profiles available in the repository
+         * @return a promise that resolves to a map of profiles available online
          */
-        static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene, forceProfile?: string): WebXRAbstractMotionController;
+        static UpdateProfilesList(): Promise<{
+            [profile: string]: string;
+        }>;
         /**
          * Find a fallback profile if the profile was not found. There are a few predefined generic profiles.
          * @param profileId the profile to which a fallback needs to be found
@@ -43850,6 +44001,15 @@ declare module BABYLON {
          * This can be used when creating your own profile or when testing different controllers
          */
         forceControllerProfile?: string;
+        /**
+         * Do not load the controller mesh, in case a different mesh needs to be loaded.
+         */
+        doNotLoadControllerMesh?: boolean;
+        /**
+         * Should the controller mesh be animated when a user interacts with it
+         * The pressed buttons / thumbstick and touchpad animations will be disabled
+         */
+        disableMotionControllerAnimation?: boolean;
     }
     /**
      * Represents an XR controller
@@ -43874,6 +44034,10 @@ declare module BABYLON {
          */
         motionController?: WebXRAbstractMotionController;
         /**
+         * Observers registered here will trigger when a motion controller profile was assigned to this xr controller
+         */
+        onMotionControllerProfileLoaded: Observable<WebXRAbstractMotionController>;
+        /**
          * Event that fires when the controller is removed/disposed
          */
         onDisposeObservable: Observable<{}>;
@@ -43926,6 +44090,20 @@ declare module BABYLON {
          * Profiles are defined here - https://github.com/immersive-web/webxr-input-profiles/
          */
         forceInputProfile?: string;
+        /**
+         * Do not send a request to the controlle repository to load the profile.
+         *
+         * Instead, use the controllers available in babylon itself.
+         */
+        disableOnlineControllerRepository?: boolean;
+        /**
+         * A custom URL for the controllers repository
+         */
+        customControllersRepositoryURL?: string;
+        /**
+         * Should the controller model's components not move according to the user input
+         */
+        disableControllerAnimation?: boolean;
     }
     /**
      * XR input used to track XR inputs such as controllers/rays
@@ -44369,13 +44547,17 @@ declare module BABYLON {
              */
             teleportationBorderColor?: string;
             /**
-             * Override the default material of the torus and arrow
-             */
-            torusArrowMaterial?: Material;
-            /**
              * Disable the mesh's animation sequence
              */
             disableAnimation?: boolean;
+            /**
+             * Disable lighting on the material or the ring and arrow
+             */
+            disableLighting?: boolean;
+            /**
+             * Override the default material of the torus and arrow
+             */
+            torusArrowMaterial?: Material;
         };
         /**
          * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
@@ -44426,6 +44608,10 @@ declare module BABYLON {
          */
         rotationAngle: number;
         /**
+         * Is movement backwards enabled
+         */
+        backwardsMovementEnabled: boolean;
+        /**
          * Distance to travel when moving backwards
          */
         backwardsTeleportationDistance: number;
@@ -45455,8 +45641,6 @@ declare module BABYLON {
         };
         constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness);
         protected _processLoadedModel(_meshes: AbstractMesh[]): void;
-        private _getChildByName;
-        private _getImmediateChildByName;
         protected _getFilenameAndPath(): {
             filename: string;
             path: string;
@@ -45522,7 +45706,13 @@ declare module BABYLON {
         static MODEL_FILENAME: string;
         profileId: string;
         private _modelRootNode;
-        constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness, legacyMapping?: boolean);
+        /**
+         * Create a new Vive motion controller object
+         * @param scene the scene to use to create this controller
+         * @param gamepadObject the corresponding gamepad object
+         * @param handness the handness of the controller
+         */
+        constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness);
         protected _processLoadedModel(_meshes: AbstractMesh[]): void;
         protected _getFilenameAndPath(): {
             filename: string;
@@ -45826,7 +46016,7 @@ declare module BABYLON {
          * Gets the camera that is used to render the utility layer (when not set, this will be the last active camera)
          * @returns the camera that is used when rendering the utility layer
          */
-        getRenderCamera(): Nullable<Camera>;
+        getRenderCamera(): Camera;
         /**
          * Sets the camera that should be used when rendering the utility layer (If set to null the last active camera will be used)
          * @param cam the camera that should be used when rendering the utility layer
@@ -46430,42 +46620,6 @@ declare module BABYLON {
         }, scene?: Nullable<Scene>): Mesh;
     }
 }
-declare module BABYLON {
-    /**
-     * Class containing static functions to help procedurally build meshes
-     */
-    export class SphereBuilder {
-        /**
-         * Creates a sphere mesh
-         * * The parameter `diameter` sets the diameter size (float) of the sphere (default 1)
-         * * You can set some different sphere dimensions, for instance to build an ellipsoid, by using the parameters `diameterX`, `diameterY` and `diameterZ` (all by default have the same value of `diameter`)
-         * * The parameter `segments` sets the sphere number of horizontal stripes (positive integer, default 32)
-         * * You can create an unclosed sphere with the parameter `arc` (positive float, default 1), valued between 0 and 1, what is the ratio of the circumference (latitude) : 2 x PI x ratio
-         * * You can create an unclosed sphere on its height with the parameter `slice` (positive float, default1), valued between 0 and 1, what is the height ratio (longitude)
-         * * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
-         * * If you create a double-sided mesh, you can choose what parts of the texture image to crop and stick respectively on the front and the back sides with the parameters `frontUVs` and `backUVs` (Vector4). Detail here : https://doc.babylonjs.com/babylon101/discover_basic_elements#side-orientation
-         * * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created
-         * @param name defines the name of the mesh
-         * @param options defines the options used to create the mesh
-         * @param scene defines the hosting scene
-         * @returns the sphere mesh
-         * @see https://doc.babylonjs.com/how_to/set_shapes#sphere
-         */
-        static CreateSphere(name: string, options: {
-            segments?: number;
-            diameter?: number;
-            diameterX?: number;
-            diameterY?: number;
-            diameterZ?: number;
-            arc?: number;
-            slice?: number;
-            sideOrientation?: number;
-            frontUVs?: Vector4;
-            backUVs?: Vector4;
-            updatable?: boolean;
-        }, scene?: Nullable<Scene>): Mesh;
-    }
-}
 declare module BABYLON.Debug {
     /**
          * Used to show the physics impostor around the specific mesh
@@ -47606,7 +47760,7 @@ declare module BABYLON {
          * @param data The array buffer containing the .env bytes.
          * @returns the environment file info (the json header) if successfully parsed.
          */
-        static GetEnvInfo(data: ArrayBuffer): Nullable<EnvironmentTextureInfo>;
+        static GetEnvInfo(data: ArrayBufferView): Nullable<EnvironmentTextureInfo>;
         /**
          * Creates an environment texture from a loaded cube texture.
          * @param texture defines the cube texture to convert in env file
@@ -47621,19 +47775,19 @@ declare module BABYLON {
         private static _CreateEnvTextureIrradiance;
         /**
          * Creates the ArrayBufferViews used for initializing environment texture image data.
-         * @param arrayBuffer the underlying ArrayBuffer to which the views refer
+         * @param data the image data
          * @param info parameters that determine what views will be created for accessing the underlying buffer
          * @return the views described by info providing access to the underlying buffer
          */
-        static CreateImageDataArrayBufferViews(arrayBuffer: any, info: EnvironmentTextureInfo): Array<Array<ArrayBufferView>>;
+        static CreateImageDataArrayBufferViews(data: ArrayBufferView, info: EnvironmentTextureInfo): Array<Array<ArrayBufferView>>;
         /**
          * Uploads the texture info contained in the env file to the GPU.
          * @param texture defines the internal texture to upload to
-         * @param arrayBuffer defines the buffer cotaining the data to load
+         * @param data defines the data to load
          * @param info defines the texture info retrieved through the GetEnvInfo method
          * @returns a promise
          */
-        static UploadEnvLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void>;
+        static UploadEnvLevelsAsync(texture: InternalTexture, data: ArrayBufferView, info: EnvironmentTextureInfo): Promise<void>;
         private static _OnImageReadyAsync;
         /**
          * Uploads the levels of image data to the GPU.
@@ -52164,10 +52318,10 @@ declare module BABYLON {
         static StoreLODInAlphaChannel: boolean;
         /**
          * Gets DDS information from an array buffer
-         * @param arrayBuffer defines the array buffer to read data from
+         * @param data defines the array buffer view to read data from
          * @returns the DDS information
          */
-        static GetDDSInfo(arrayBuffer: any): DDSInfo;
+        static GetDDSInfo(data: ArrayBufferView): DDSInfo;
         private static _FloatView;
         private static _Int32View;
         private static _ToHalfFloat;
@@ -52185,7 +52339,7 @@ declare module BABYLON {
          * Uploads DDS Levels to a Babylon Texture
          * @hidden
          */
-        static UploadDDSLevels(engine: ThinEngine, texture: InternalTexture, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex?: number, currentFace?: number): void;
+        static UploadDDSLevels(engine: ThinEngine, texture: InternalTexture, data: ArrayBufferView, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex?: number, currentFace?: number): void;
     }
         interface ThinEngine {
             /**
@@ -52246,14 +52400,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(imgs: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(imgs: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {
@@ -52298,14 +52452,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {
@@ -52315,7 +52469,7 @@ declare module BABYLON {
      */
     export class KhronosTextureContainer {
         /** contents of the KTX container file */
-        arrayBuffer: any;
+        data: ArrayBufferView;
         private static HEADER_LEN;
         private static COMPRESSED_2D;
         private static COMPRESSED_3D;
@@ -52379,14 +52533,14 @@ declare module BABYLON {
         isInvalid: boolean;
         /**
          * Creates a new KhronosTextureContainer
-         * @param arrayBuffer contents of the KTX container file
+         * @param data contents of the KTX container file
          * @param facesExpected should be either 1 or 6, based whether a cube texture or or
          * @param threeDExpected provision for indicating that data should be a 3D texture, not implemented
          * @param textureArrayExpected provision for indicating that data should be a texture array, not implemented
          */
         constructor(
         /** contents of the KTX container file */
-        arrayBuffer: any, facesExpected: number, threeDExpected?: boolean, textureArrayExpected?: boolean);
+        data: ArrayBufferView, facesExpected: number, threeDExpected?: boolean, textureArrayExpected?: boolean);
         /**
          * Uploads KTX content to a Babylon Texture.
          * It is assumed that the texture has already been created & is currently bound
@@ -52438,14 +52592,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void;
     }
 }
 declare module BABYLON {
@@ -56196,14 +56350,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {
@@ -56299,11 +56453,11 @@ declare module BABYLON {
         private static _CreateWorkerAsync;
         /**
          * Transcodes a loaded image file to compressed pixel data
-         * @param imageData image data to transcode
+         * @param data image data to transcode
          * @param config configuration options for the transcoding
          * @returns a promise resulting in the transcoded image
          */
-        static TranscodeAsync(imageData: ArrayBuffer, config: BasisTranscodeConfiguration): Promise<TranscodeResult>;
+        static TranscodeAsync(data: ArrayBuffer | ArrayBufferView, config: BasisTranscodeConfiguration): Promise<TranscodeResult>;
         /**
          * Loads a texture from the transcode result
          * @param texture texture load to
@@ -56353,14 +56507,14 @@ declare module BABYLON {
          * @param onLoad defines the callback to trigger once the texture is ready
          * @param onError defines the callback to trigger in case of error
          */
-        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+        loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
         /**
          * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
          * @param data contains the texture data
          * @param texture defines the BabylonJS internal texture
          * @param callback defines the method to call once ready to upload
          */
-        loadData(data: ArrayBuffer, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+        loadData(data: ArrayBufferView, texture: InternalTexture, callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
     }
 }
 declare module BABYLON {

+ 25 - 15
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -3356,17 +3356,27 @@ var GLTFFileLoader = /** @class */ (function () {
             JSON: 0x4E4F534A,
             BIN: 0x004E4942
         };
-        var data = { json: {}, bin: null };
-        var readAsync = function () {
-            var chunkLength = dataReader.readUint32();
-            var chunkFormat = dataReader.readUint32();
-            var finalChunk = (dataReader.byteOffset + chunkLength + 8 > length);
-            // Read the chunk and (if available) the length and type of the next chunk.
-            return dataReader.loadAsync(finalChunk ? chunkLength : chunkLength + 8).then(function () {
+        // Read the JSON chunk header.
+        var chunkLength = dataReader.readUint32();
+        var chunkFormat = dataReader.readUint32();
+        if (chunkFormat !== ChunkFormat.JSON) {
+            throw new Error("First chunk format is not JSON");
+        }
+        // Bail if there are no other chunks.
+        if (dataReader.byteOffset + chunkLength === dataReader.buffer.byteLength) {
+            return dataReader.loadAsync(chunkLength).then(function () {
+                return { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            });
+        }
+        // Read the JSON chunk and the length and type of the next chunk.
+        return dataReader.loadAsync(chunkLength + 8).then(function () {
+            var data = { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            var readAsync = function () {
+                var chunkLength = dataReader.readUint32();
+                var chunkFormat = dataReader.readUint32();
                 switch (chunkFormat) {
                     case ChunkFormat.JSON: {
-                        data.json = _this._parseJson(dataReader.readString(chunkLength));
-                        break;
+                        throw new Error("Unexpected JSON chunk");
                     }
                     case ChunkFormat.BIN: {
                         var startByteOffset_2 = dataReader.byteOffset;
@@ -3383,13 +3393,13 @@ var GLTFFileLoader = /** @class */ (function () {
                         break;
                     }
                 }
-                if (finalChunk) {
-                    return data;
+                if (dataReader.byteOffset !== dataReader.buffer.byteLength) {
+                    return dataReader.loadAsync(8).then(readAsync);
                 }
-                return readAsync();
-            });
-        };
-        return readAsync();
+                return Promise.resolve(data);
+            };
+            return readAsync();
+        });
     };
     GLTFFileLoader._parseVersion = function (version) {
         if (version === "1.0" || version === "1.0.1") {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/loaders/babylon.glTF1FileLoader.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 25 - 15
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -4606,17 +4606,27 @@ var GLTFFileLoader = /** @class */ (function () {
             JSON: 0x4E4F534A,
             BIN: 0x004E4942
         };
-        var data = { json: {}, bin: null };
-        var readAsync = function () {
-            var chunkLength = dataReader.readUint32();
-            var chunkFormat = dataReader.readUint32();
-            var finalChunk = (dataReader.byteOffset + chunkLength + 8 > length);
-            // Read the chunk and (if available) the length and type of the next chunk.
-            return dataReader.loadAsync(finalChunk ? chunkLength : chunkLength + 8).then(function () {
+        // Read the JSON chunk header.
+        var chunkLength = dataReader.readUint32();
+        var chunkFormat = dataReader.readUint32();
+        if (chunkFormat !== ChunkFormat.JSON) {
+            throw new Error("First chunk format is not JSON");
+        }
+        // Bail if there are no other chunks.
+        if (dataReader.byteOffset + chunkLength === dataReader.buffer.byteLength) {
+            return dataReader.loadAsync(chunkLength).then(function () {
+                return { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            });
+        }
+        // Read the JSON chunk and the length and type of the next chunk.
+        return dataReader.loadAsync(chunkLength + 8).then(function () {
+            var data = { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            var readAsync = function () {
+                var chunkLength = dataReader.readUint32();
+                var chunkFormat = dataReader.readUint32();
                 switch (chunkFormat) {
                     case ChunkFormat.JSON: {
-                        data.json = _this._parseJson(dataReader.readString(chunkLength));
-                        break;
+                        throw new Error("Unexpected JSON chunk");
                     }
                     case ChunkFormat.BIN: {
                         var startByteOffset_2 = dataReader.byteOffset;
@@ -4633,13 +4643,13 @@ var GLTFFileLoader = /** @class */ (function () {
                         break;
                     }
                 }
-                if (finalChunk) {
-                    return data;
+                if (dataReader.byteOffset !== dataReader.buffer.byteLength) {
+                    return dataReader.loadAsync(8).then(readAsync);
                 }
-                return readAsync();
-            });
-        };
-        return readAsync();
+                return Promise.resolve(data);
+            };
+            return readAsync();
+        });
     };
     GLTFFileLoader._parseVersion = function (version) {
         if (version === "1.0" || version === "1.0.1") {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 25 - 15
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -7166,17 +7166,27 @@ var GLTFFileLoader = /** @class */ (function () {
             JSON: 0x4E4F534A,
             BIN: 0x004E4942
         };
-        var data = { json: {}, bin: null };
-        var readAsync = function () {
-            var chunkLength = dataReader.readUint32();
-            var chunkFormat = dataReader.readUint32();
-            var finalChunk = (dataReader.byteOffset + chunkLength + 8 > length);
-            // Read the chunk and (if available) the length and type of the next chunk.
-            return dataReader.loadAsync(finalChunk ? chunkLength : chunkLength + 8).then(function () {
+        // Read the JSON chunk header.
+        var chunkLength = dataReader.readUint32();
+        var chunkFormat = dataReader.readUint32();
+        if (chunkFormat !== ChunkFormat.JSON) {
+            throw new Error("First chunk format is not JSON");
+        }
+        // Bail if there are no other chunks.
+        if (dataReader.byteOffset + chunkLength === dataReader.buffer.byteLength) {
+            return dataReader.loadAsync(chunkLength).then(function () {
+                return { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            });
+        }
+        // Read the JSON chunk and the length and type of the next chunk.
+        return dataReader.loadAsync(chunkLength + 8).then(function () {
+            var data = { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            var readAsync = function () {
+                var chunkLength = dataReader.readUint32();
+                var chunkFormat = dataReader.readUint32();
                 switch (chunkFormat) {
                     case ChunkFormat.JSON: {
-                        data.json = _this._parseJson(dataReader.readString(chunkLength));
-                        break;
+                        throw new Error("Unexpected JSON chunk");
                     }
                     case ChunkFormat.BIN: {
                         var startByteOffset_2 = dataReader.byteOffset;
@@ -7193,13 +7203,13 @@ var GLTFFileLoader = /** @class */ (function () {
                         break;
                     }
                 }
-                if (finalChunk) {
-                    return data;
+                if (dataReader.byteOffset !== dataReader.buffer.byteLength) {
+                    return dataReader.loadAsync(8).then(readAsync);
                 }
-                return readAsync();
-            });
-        };
-        return readAsync();
+                return Promise.resolve(data);
+            };
+            return readAsync();
+        });
     };
     GLTFFileLoader._parseVersion = function (version) {
         if (version === "1.0" || version === "1.0.1") {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 25 - 15
dist/preview release/loaders/babylonjs.loaders.js

@@ -8533,17 +8533,27 @@ var GLTFFileLoader = /** @class */ (function () {
             JSON: 0x4E4F534A,
             BIN: 0x004E4942
         };
-        var data = { json: {}, bin: null };
-        var readAsync = function () {
-            var chunkLength = dataReader.readUint32();
-            var chunkFormat = dataReader.readUint32();
-            var finalChunk = (dataReader.byteOffset + chunkLength + 8 > length);
-            // Read the chunk and (if available) the length and type of the next chunk.
-            return dataReader.loadAsync(finalChunk ? chunkLength : chunkLength + 8).then(function () {
+        // Read the JSON chunk header.
+        var chunkLength = dataReader.readUint32();
+        var chunkFormat = dataReader.readUint32();
+        if (chunkFormat !== ChunkFormat.JSON) {
+            throw new Error("First chunk format is not JSON");
+        }
+        // Bail if there are no other chunks.
+        if (dataReader.byteOffset + chunkLength === dataReader.buffer.byteLength) {
+            return dataReader.loadAsync(chunkLength).then(function () {
+                return { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            });
+        }
+        // Read the JSON chunk and the length and type of the next chunk.
+        return dataReader.loadAsync(chunkLength + 8).then(function () {
+            var data = { json: _this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            var readAsync = function () {
+                var chunkLength = dataReader.readUint32();
+                var chunkFormat = dataReader.readUint32();
                 switch (chunkFormat) {
                     case ChunkFormat.JSON: {
-                        data.json = _this._parseJson(dataReader.readString(chunkLength));
-                        break;
+                        throw new Error("Unexpected JSON chunk");
                     }
                     case ChunkFormat.BIN: {
                         var startByteOffset_2 = dataReader.byteOffset;
@@ -8560,13 +8570,13 @@ var GLTFFileLoader = /** @class */ (function () {
                         break;
                     }
                 }
-                if (finalChunk) {
-                    return data;
+                if (dataReader.byteOffset !== dataReader.buffer.byteLength) {
+                    return dataReader.loadAsync(8).then(readAsync);
                 }
-                return readAsync();
-            });
-        };
-        return readAsync();
+                return Promise.resolve(data);
+            };
+            return readAsync();
+        });
     };
     GLTFFileLoader._parseVersion = function (version) {
         if (version === "1.0" || version === "1.0.1") {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 2 - 2
dist/preview release/loaders/babylonjs.loaders.min.js


+ 28 - 28
dist/preview release/nodeEditor/babylon.nodeEditor.max.js

@@ -7,7 +7,7 @@
 		exports["babylonjs-node-editor"] = factory(require("babylonjs"));
 	else
 		root["NODEEDITOR"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_Textures_texture__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -51129,7 +51129,7 @@ module.exports = function(module) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BlockTools", function() { return BlockTools; });
-/* harmony import */ var babylonjs_Materials_Node_Blocks_Fragment_discardBlock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/Fragment/discardBlock */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Blocks_Fragment_discardBlock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/Fragment/discardBlock */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Blocks_Fragment_discardBlock__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Blocks_Fragment_discardBlock__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -51930,7 +51930,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _fortawesome_react_fontawesome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @fortawesome/react-fontawesome */ "../../node_modules/@fortawesome/react-fontawesome/index.es.js");
 /* harmony import */ var _fortawesome_free_solid_svg_icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @fortawesome/free-solid-svg-icons */ "../../node_modules/@fortawesome/free-solid-svg-icons/index.es.js");
-/* harmony import */ var babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Maths/math.color */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Maths/math.color */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var _dataStorage__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../dataStorage */ "./dataStorage.ts");
 
@@ -52028,7 +52028,7 @@ var PreviewAreaComponent = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PreviewManager", function() { return PreviewManager; });
-/* harmony import */ var babylonjs_Materials_Node_nodeMaterial__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/nodeMaterial */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_nodeMaterial__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/nodeMaterial */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_nodeMaterial__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_nodeMaterial__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _previewMeshType__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./previewMeshType */ "./components/preview/previewMeshType.ts");
 /* harmony import */ var _log_logComponent__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../log/logComponent */ "./components/log/logComponent.tsx");
@@ -52717,7 +52717,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _sharedComponents_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../sharedComponents/lineContainerComponent */ "./sharedComponents/lineContainerComponent.tsx");
 /* harmony import */ var _stringTools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../stringTools */ "./stringTools.ts");
 /* harmony import */ var _sharedComponents_fileButtonLineComponent__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../sharedComponents/fileButtonLineComponent */ "./sharedComponents/fileButtonLineComponent.tsx");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_6__);
 /* harmony import */ var _serializationTools__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../serializationTools */ "./serializationTools.ts");
 /* harmony import */ var _sharedComponents_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../sharedComponents/checkBoxLineComponent */ "./sharedComponents/checkBoxLineComponent.tsx");
@@ -53071,7 +53071,7 @@ var GradientDisplayManager = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputDisplayManager", function() { return InputDisplayManager; });
-/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialSystemValues__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialSystemValues */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialSystemValues__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialSystemValues */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialSystemValues__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Enums_nodeMaterialSystemValues__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _blockTools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../blockTools */ "./blockTools.ts");
 /* harmony import */ var _stringTools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../stringTools */ "./stringTools.ts");
@@ -53288,7 +53288,7 @@ var RemapDisplayManager = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextureDisplayManager", function() { return TextureDisplayManager; });
-/* harmony import */ var babylonjs_Materials_Node_Blocks_Dual_textureBlock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/Dual/textureBlock */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Blocks_Dual_textureBlock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/Dual/textureBlock */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Blocks_Dual_textureBlock__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Blocks_Dual_textureBlock__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _sharedComponents_textureLineComponent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../sharedComponents/textureLineComponent */ "./sharedComponents/textureLineComponent.tsx");
 
@@ -53354,7 +53354,7 @@ var TextureDisplayManager = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TrigonometryDisplayManager", function() { return TrigonometryDisplayManager; });
-/* harmony import */ var babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/trigonometryBlock */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/trigonometryBlock */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_0__);
 
 var TrigonometryDisplayManager = /** @class */ (function () {
@@ -53475,7 +53475,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var dagre__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! dagre */ "../../node_modules/dagre/index.js");
 /* harmony import */ var dagre__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(dagre__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var _nodeLink__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./nodeLink */ "./diagram/nodeLink.ts");
-/* harmony import */ var babylonjs_Materials_Node_nodeMaterialBlockConnectionPoint__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_nodeMaterialBlockConnectionPoint__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_nodeMaterialBlockConnectionPoint__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_nodeMaterialBlockConnectionPoint__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var _dataStorage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../dataStorage */ "./dataStorage.ts");
 /* harmony import */ var _graphFrame__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./graphFrame */ "./diagram/graphFrame.ts");
@@ -54182,7 +54182,7 @@ var GraphCanvasComponent = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GraphFrame", function() { return GraphFrame; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* 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 _nodePort__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./nodePort */ "./diagram/nodePort.ts");
 /* harmony import */ var _serializationTools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../serializationTools */ "./serializationTools.ts");
@@ -55092,7 +55092,7 @@ var GraphNode = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NodeLink", function() { return NodeLink; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 
 var NodeLink = /** @class */ (function () {
@@ -55246,7 +55246,7 @@ var NodeLink = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NodePort", function() { return NodePort; });
 /* harmony import */ var _blockTools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../blockTools */ "./blockTools.ts");
-/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -55539,7 +55539,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 _sharedComponents_lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../sharedComponents/lineContainerComponent */ "./sharedComponents/lineContainerComponent.tsx");
-/* harmony import */ var babylonjs_Materials_Node_Blocks_gradientBlock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/gradientBlock */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Blocks_gradientBlock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/gradientBlock */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Blocks_gradientBlock__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Blocks_gradientBlock__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var _gradientStepComponent__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./gradientStepComponent */ "./diagram/properties/gradientStepComponent.tsx");
 /* harmony import */ var _sharedComponents_buttonLineComponent__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../sharedComponents/buttonLineComponent */ "./sharedComponents/buttonLineComponent.tsx");
@@ -55624,7 +55624,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _fortawesome_react_fontawesome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @fortawesome/react-fontawesome */ "../../node_modules/@fortawesome/react-fontawesome/index.es.js");
 /* harmony import */ var _fortawesome_free_solid_svg_icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @fortawesome/free-solid-svg-icons */ "../../node_modules/@fortawesome/free-solid-svg-icons/index.es.js");
-/* harmony import */ var babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Maths/math.color */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Maths/math.color */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_color__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -55693,7 +55693,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _components_propertyTab_properties_matrixPropertyTabComponent__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../components/propertyTab/properties/matrixPropertyTabComponent */ "./components/propertyTab/properties/matrixPropertyTabComponent.tsx");
 /* harmony import */ var _sharedComponents_lineContainerComponent__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../sharedComponents/lineContainerComponent */ "./sharedComponents/lineContainerComponent.tsx");
 /* harmony import */ var _sharedComponents_optionsLineComponent__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../sharedComponents/optionsLineComponent */ "./sharedComponents/optionsLineComponent.tsx");
-/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_12__);
 /* harmony import */ var _genericNodePropertyComponent__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./genericNodePropertyComponent */ "./diagram/properties/genericNodePropertyComponent.tsx");
 /* harmony import */ var _sharedComponents_textInputLineComponent__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../../sharedComponents/textInputLineComponent */ "./sharedComponents/textInputLineComponent.tsx");
@@ -56111,7 +56111,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 _sharedComponents_fileButtonLineComponent__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../sharedComponents/fileButtonLineComponent */ "./sharedComponents/fileButtonLineComponent.tsx");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var _sharedComponents_lineContainerComponent__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../sharedComponents/lineContainerComponent */ "./sharedComponents/lineContainerComponent.tsx");
 /* harmony import */ var _sharedComponents_textInputLineComponent__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../sharedComponents/textInputLineComponent */ "./sharedComponents/textInputLineComponent.tsx");
@@ -56420,7 +56420,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _sharedComponents_lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../sharedComponents/lineContainerComponent */ "./sharedComponents/lineContainerComponent.tsx");
 /* harmony import */ var _sharedComponents_optionsLineComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../sharedComponents/optionsLineComponent */ "./sharedComponents/optionsLineComponent.tsx");
-/* harmony import */ var babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/trigonometryBlock */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/trigonometryBlock */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Blocks_trigonometryBlock__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var _genericNodePropertyComponent__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./genericNodePropertyComponent */ "./diagram/properties/genericNodePropertyComponent.tsx");
 
@@ -56580,7 +56580,7 @@ PropertyLedger.RegisteredControls["TrigonometryBlock"] = _properties_trigonometr
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GlobalState", function() { return GlobalState; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* 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 _components_preview_previewMeshType__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/preview/previewMeshType */ "./components/preview/previewMeshType.ts");
 /* harmony import */ var _dataStorage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dataStorage */ "./dataStorage.ts");
@@ -56648,7 +56648,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _portal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./portal */ "./portal.tsx");
 /* harmony import */ var _components_log_logComponent__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./components/log/logComponent */ "./components/log/logComponent.tsx");
 /* harmony import */ var _dataStorage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./dataStorage */ "./dataStorage.ts");
-/* harmony import */ var babylonjs_Materials_Node_Blocks_Input_inputBlock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/Input/inputBlock */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Blocks_Input_inputBlock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Materials/Node/Blocks/Input/inputBlock */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Blocks_Input_inputBlock__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Blocks_Input_inputBlock__WEBPACK_IMPORTED_MODULE_7__);
 /* harmony import */ var _sharedComponents_messageDialog__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sharedComponents/messageDialog */ "./sharedComponents/messageDialog.tsx");
 /* harmony import */ var _blockTools__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./blockTools */ "./blockTools.ts");
@@ -57476,7 +57476,7 @@ var Portal = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SerializationTools", function() { return SerializationTools; });
-/* harmony import */ var babylonjs_Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Textures/texture */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Textures/texture */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _dataStorage__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./dataStorage */ "./dataStorage.ts");
 
@@ -57646,7 +57646,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 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 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _numericInputComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./numericInputComponent */ "./sharedComponents/numericInputComponent.tsx");
 /* harmony import */ var _fortawesome_react_fontawesome__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @fortawesome/react-fontawesome */ "../../node_modules/@fortawesome/react-fontawesome/index.es.js");
@@ -57792,7 +57792,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 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 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _numericInputComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./numericInputComponent */ "./sharedComponents/numericInputComponent.tsx");
 /* harmony import */ var _fortawesome_react_fontawesome__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @fortawesome/react-fontawesome */ "../../node_modules/@fortawesome/react-fontawesome/index.es.js");
@@ -58165,7 +58165,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 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 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _vector4LineComponent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./vector4LineComponent */ "./sharedComponents/vector4LineComponent.tsx");
 /* harmony import */ var _optionsLineComponent__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./optionsLineComponent */ "./sharedComponents/optionsLineComponent.tsx");
@@ -58589,7 +58589,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 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 babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -58821,7 +58821,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 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 babylonjs_Engines_constants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Engines/constants */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Engines_constants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Engines/constants */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Engines_constants__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Engines_constants__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -59299,7 +59299,7 @@ var Vector4LineComponent = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StringTools", function() { return StringTools; });
-/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes */ "babylonjs/Materials/Textures/texture");
+/* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_Node_Enums_nodeMaterialBlockConnectionPointTypes__WEBPACK_IMPORTED_MODULE_0__);
 
 var StringTools = /** @class */ (function () {
@@ -59398,14 +59398,14 @@ var StringTools = /** @class */ (function () {
 
 /***/ }),
 
-/***/ "babylonjs/Materials/Textures/texture":
+/***/ "babylonjs/Misc/observable":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_Textures_texture__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__;
 
 /***/ })
 

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map


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

@@ -1 +1 @@
-{"thinEngineOnly":112368,"engineOnly":148478,"sceneOnly":503038,"minGridMaterial":633672,"minStandardMaterial":773699}
+{"thinEngineOnly":112467,"engineOnly":148577,"sceneOnly":503174,"minGridMaterial":633842,"minStandardMaterial":773869}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 632 - 316
dist/preview release/viewer/babylon.module.d.ts


Dosya farkı çok büyük olduğundan ihmal edildi
+ 269 - 265
dist/preview release/viewer/babylon.viewer.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -61,6 +61,7 @@
 - Added `textures/opacity.png` file to the Playground ([Popov72](https://github.com/Popov72))
 - Refactored the shadow generators code ([Popov72](https://github.com/Popov72))
 - Added preview area pop up for NME ([Kyle Belfort](https://github.com/belfortk))
+- Supports clip planes with shadows ([sebavan](http://www.github.com/sebavan))
 
 ### Engine
 
@@ -197,11 +198,12 @@
 - XR Camera's API is Babylon-conform (position, rotationQuaternion, world matrix, direction etc') ([#7239](https://github.com/BabylonJS/Babylon.js/issues/7239)) ([RaananW](https://github.com/RaananW/))
 - XR Input now using standard profiles and completely separated from the gamepad class ([#7348](https://github.com/BabylonJS/Babylon.js/issues/7348)) ([RaananW](https://github.com/RaananW/))
 - Teleportation and controller selection are now WebXR features. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
-- Teleportation allows selecting direction before teleporting when using thumbstick or touchpad. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
+- Teleportation allows selecting direction before teleporting when using thumbstick / touchpad. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
 - It is now possible to force a certain profile type for the controllers ([#7348](https://github.com/BabylonJS/Babylon.js/issues/7375)) ([RaananW](https://github.com/RaananW/))
 - WebXR camera is initialized on the first frame, including copying transformation from native camera (except for in AR) ([#7389](https://github.com/BabylonJS/Babylon.js/issues/7389)) ([RaananW](https://github.com/RaananW/))
 - Selection has gaze mode (which can be forced) and touch-screen support ([#7395](https://github.com/BabylonJS/Babylon.js/issues/7395)) ([RaananW](https://github.com/RaananW/))
 - Laser pointers can be excluded from lighting influence so that they are always visible in both WebXR and WebVR ([#7323](https://github.com/BabylonJS/Babylon.js/issues/7323)) ([RaananW](https://github.com/RaananW/))
+- Full support for the online motion controller repository ([#7323](https://github.com/BabylonJS/Babylon.js/issues/7323)) ([RaananW](https://github.com/RaananW/))
 
 ### Ray
 
@@ -303,6 +305,7 @@
 - Physics compound calculations were incorrect ([#7407](https://github.com/BabylonJS/Babylon.js/issues/7407)) ([RaananW](https://github.com/RaananW/))
 - Fix bug NME bug where preview area crashes on pop up when NME is opened from playground ([Kyle Belfort](https://github.com/belfortk))
 - Fixed an issue with isSessionSupported return value being ignored ([#7501](https://github.com/BabylonJS/Babylon.js/issues/7501)) ([RaananW](https://github.com/RaananW/))
+- Added isRigCamera to rig cameras so they can be detected. Used to fix a bug with utility layer and WebXR ([#7517](https://github.com/BabylonJS/Babylon.js/issues/7517)) ([RaananW](https://github.com/RaananW/))
 
 ## Breaking changes
 

+ 26 - 15
loaders/src/glTF/glTFFileLoader.ts

@@ -787,20 +787,31 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
             BIN: 0x004E4942
         };
 
-        const data: IGLTFLoaderData = { json: {}, bin: null };
+        // Read the JSON chunk header.
+        const chunkLength = dataReader.readUint32();
+        const chunkFormat = dataReader.readUint32();
+        if (chunkFormat !== ChunkFormat.JSON) {
+            throw new Error("First chunk format is not JSON");
+        }
 
-        const readAsync = (): Promise<IGLTFLoaderData> => {
-            const chunkLength = dataReader.readUint32();
-            const chunkFormat = dataReader.readUint32();
+        // Bail if there are no other chunks.
+        if (dataReader.byteOffset + chunkLength === dataReader.buffer.byteLength) {
+            return dataReader.loadAsync(chunkLength).then(() => {
+                return { json: this._parseJson(dataReader.readString(chunkLength)), bin: null };
+            });
+        }
 
-            const finalChunk = (dataReader.byteOffset + chunkLength + 8 > length);
+        // Read the JSON chunk and the length and type of the next chunk.
+        return dataReader.loadAsync(chunkLength + 8).then(() => {
+            const data: IGLTFLoaderData = { json: this._parseJson(dataReader.readString(chunkLength)), bin: null };
+
+            const readAsync = (): Promise<IGLTFLoaderData> => {
+                const chunkLength = dataReader.readUint32();
+                const chunkFormat = dataReader.readUint32();
 
-            // Read the chunk and (if available) the length and type of the next chunk.
-            return dataReader.loadAsync(finalChunk ? chunkLength : chunkLength + 8).then(() => {
                 switch (chunkFormat) {
                     case ChunkFormat.JSON: {
-                        data.json = this._parseJson(dataReader.readString(chunkLength));
-                        break;
+                        throw new Error("Unexpected JSON chunk");
                     }
                     case ChunkFormat.BIN: {
                         const startByteOffset = dataReader.byteOffset;
@@ -818,15 +829,15 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
                     }
                 }
 
-                if (finalChunk) {
-                    return data;
+                if (dataReader.byteOffset !== dataReader.buffer.byteLength) {
+                    return dataReader.loadAsync(8).then(readAsync);
                 }
 
-                return readAsync();
-            });
-        };
+                return Promise.resolve(data);
+            };
 
-        return readAsync();
+            return readAsync();
+        });
     }
 
     private static _parseVersion(version: string): Nullable<{ major: number, minor: number }> {

+ 36 - 38
src/Cameras/XR/features/WebXRControllerPointerSelection.ts

@@ -357,53 +357,51 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature {
     }
 
     private _attachTrackedPointerRayMode(xrController: WebXRController) {
-        if (!xrController.motionController) {
-            return;
-        }
-
-        if (this._options.forceGazeMode) {
-            return this._attachGazeMode(xrController);
-        }
-
-        const controllerData = this._controllers[xrController.uniqueId];
-
-        if (this._options.overrideButtonId) {
-            controllerData.selectionComponent = xrController.motionController.getComponent(this._options.overrideButtonId);
-        }
-        if (!controllerData.selectionComponent) {
-            controllerData.selectionComponent = xrController.motionController.getMainComponent();
-        }
-
-        controllerData.onFrameObserver = this._xrSessionManager.onXRFrameObservable.add(() => {
-            if (controllerData.selectionComponent && controllerData.selectionComponent.pressed) {
-                (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshPickedColor;
-                (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.laserPointerPickedColor;
-            } else {
-                (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshDefaultColor;
-                (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.lasterPointerDefaultColor;
+        xrController.onMotionControllerProfileLoaded.add((motionController) => {
+            if (this._options.forceGazeMode) {
+                return this._attachGazeMode(xrController);
             }
 
-            controllerData.laserPointer.isVisible = this.displayLaserPointer;
-            (<StandardMaterial>controllerData.laserPointer.material).disableLighting = this.disablePointerLighting;
-            (<StandardMaterial>controllerData.selectionMesh.material).disableLighting = this.disableSelectionMeshLighting;
+            const controllerData = this._controllers[xrController.uniqueId];
 
-            if (controllerData.pick) {
-                this._scene.simulatePointerMove(controllerData.pick, { pointerId: controllerData.id });
+            if (this._options.overrideButtonId) {
+                controllerData.selectionComponent = motionController.getComponent(this._options.overrideButtonId);
+            }
+            if (!controllerData.selectionComponent) {
+                controllerData.selectionComponent = motionController.getMainComponent();
             }
-        });
 
-        controllerData.onButtonChangedObserver = controllerData.selectionComponent.onButtonStateChanged.add((component) => {
-            if (component.changes.pressed) {
-                const pressed = component.changes.pressed.current;
+            controllerData.onFrameObserver = this._xrSessionManager.onXRFrameObservable.add(() => {
+                if (controllerData.selectionComponent && controllerData.selectionComponent.pressed) {
+                    (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshPickedColor;
+                    (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.laserPointerPickedColor;
+                } else {
+                    (<StandardMaterial>controllerData.selectionMesh.material).emissiveColor = this.selectionMeshDefaultColor;
+                    (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.lasterPointerDefaultColor;
+                }
+                controllerData.laserPointer.isVisible = this.displayLaserPointer;
+                (<StandardMaterial>controllerData.laserPointer.material).disableLighting = this.disablePointerLighting;
+                (<StandardMaterial>controllerData.selectionMesh.material).disableLighting = this.disableSelectionMeshLighting;
+
                 if (controllerData.pick) {
-                    if (pressed) {
-                        this._scene.simulatePointerDown(controllerData.pick, { pointerId: controllerData.id });
-                    } else {
-                        this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+                    this._scene.simulatePointerMove(controllerData.pick, { pointerId: controllerData.id });
+                }
+            });
+
+            controllerData.onButtonChangedObserver = controllerData.selectionComponent.onButtonStateChanged.add((component) => {
+                if (component.changes.pressed) {
+                    const pressed = component.changes.pressed.current;
+                    if (controllerData.pick) {
+                        if (pressed) {
+                            this._scene.simulatePointerDown(controllerData.pick, { pointerId: controllerData.id });
+                        } else {
+                            this._scene.simulatePointerUp(controllerData.pick, { pointerId: controllerData.id });
+                        }
                     }
                 }
-            }
+            });
         });
+
     }
 
     private _detachController(xrControllerUniqueId: string) {

+ 129 - 104
src/Cameras/XR/features/WebXRControllerTeleportation.ts

@@ -21,6 +21,7 @@ import { PickingInfo } from '../../../Collisions/pickingInfo';
 import { Curve3 } from '../../../Maths/math.path';
 import { LinesBuilder } from '../../../Meshes/Builders/linesBuilder';
 import { WebXRAbstractFeature } from './WebXRAbstractFeature';
+import { Color3 } from '../../../Maths/math.color';
 
 /**
  * The options container for the teleportation module
@@ -57,13 +58,17 @@ export interface IWebXRTeleportationOptions {
          */
         teleportationBorderColor?: string;
         /**
-         * Override the default material of the torus and arrow
-         */
-        torusArrowMaterial?: Material;
-        /**
          * Disable the mesh's animation sequence
          */
         disableAnimation?: boolean;
+        /**
+         * Disable lighting on the material or the ring and arrow
+         */
+        disableLighting?: boolean;
+        /**
+         * Override the default material of the torus and arrow
+         */
+        torusArrowMaterial?: Material;
     };
 
     /**
@@ -118,9 +123,14 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
     public rotationAngle: number = Math.PI / 8;
 
     /**
+     * Is movement backwards enabled
+     */
+    public backwardsMovementEnabled = true;
+
+    /**
      * Distance to travel when moving backwards
      */
-    public backwardsTeleportationDistance: number = 0.5;
+    public backwardsTeleportationDistance: number = 0.7;
 
     /**
      * Add a new mesh to the floor meshes array
@@ -316,112 +326,116 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
         };
         const controllerData = this._controllers[xrController.uniqueId];
         // motion controller support
-        if (xrController.motionController) {
-            const movementController = xrController.motionController.getComponent(WebXRControllerComponent.THUMBSTICK) || xrController.motionController.getComponent(WebXRControllerComponent.TOUCHPAD);
-            if (!movementController || this._options.useMainComponentOnly) {
-                // use trigger to move on long press
-                const mainComponent = xrController.motionController.getMainComponent();
-                if (!mainComponent) {
-                    return;
-                }
-                controllerData.onButtonChangedObserver = mainComponent.onButtonStateChanged.add(() => {
-                    // did "pressed" changed?
-                    if (mainComponent.changes.pressed) {
-                        if (mainComponent.changes.pressed.current) {
-                            // simulate "forward" thumbstick push
+        xrController.onMotionControllerProfileLoaded.addOnce(() => {
+            if (xrController.motionController) {
+                const movementController = xrController.motionController.getComponentOfType(WebXRControllerComponent.THUMBSTICK) || xrController.motionController.getComponentOfType(WebXRControllerComponent.TOUCHPAD);
+                if (!movementController || this._options.useMainComponentOnly) {
+                    // use trigger to move on long press
+                    const mainComponent = xrController.motionController.getMainComponent();
+                    if (!mainComponent) {
+                        return;
+                    }
+                    controllerData.onButtonChangedObserver = mainComponent.onButtonStateChanged.add(() => {
+                        // did "pressed" changed?
+                        if (mainComponent.changes.pressed) {
+                            if (mainComponent.changes.pressed.current) {
+                                // simulate "forward" thumbstick push
+                                controllerData.teleportationState.forward = true;
+                                this._currentTeleportationControllerId = controllerData.xrController.uniqueId;
+                                controllerData.teleportationState.baseRotation = this._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y;
+                                controllerData.teleportationState.currentRotation = 0;
+                                const timeToSelect = this._options.timeToTeleport || 3000;
+                                let timer = 0;
+                                const observer = this._xrSessionManager.onXRFrameObservable.add(() => {
+                                    if (!mainComponent.pressed) {
+                                        this._xrSessionManager.onXRFrameObservable.remove(observer);
+                                        return;
+                                    }
+                                    timer += this._xrSessionManager.scene.getEngine().getDeltaTime();
+                                    if (timer >= timeToSelect && this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward) {
+                                        this._teleportForward(xrController.uniqueId);
+                                    }
+
+                                    // failsafe
+                                    if (timer >= timeToSelect) {
+                                        this._xrSessionManager.onXRFrameObservable.remove(observer);
+                                    }
+                                });
+                            } else {
+                                controllerData.teleportationState.forward = false;
+                                this._currentTeleportationControllerId = "";
+                            }
+                        }
+                    });
+                } else {
+                    controllerData.onButtonChangedObserver = movementController.onButtonStateChanged.add(() => {
+                        if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward && !movementController.touched) {
+                            this._teleportForward(xrController.uniqueId);
+                        }
+                    });
+                    // use thumbstick (or touchpad if thumbstick not available)
+                    controllerData.onAxisChangedObserver = movementController.onAxisValueChanged.add((axesData) => {
+                        if (axesData.y <= 0.7 && controllerData.teleportationState.backwards) {
+                            //if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId) {
+                            controllerData.teleportationState.backwards = false;
+                            //this._currentTeleportationControllerId = "";
+                            //}
+                        }
+                        if (axesData.y > 0.7 && !controllerData.teleportationState.forward && this.backwardsMovementEnabled) {
+                            // teleport backwards
+                            if (!controllerData.teleportationState.backwards) {
+                                controllerData.teleportationState.backwards = true;
+                                // teleport backwards ONCE
+                                this._tmpVector.set(0, 0, this.backwardsTeleportationDistance!);
+                                this._tmpVector.rotateByQuaternionToRef(this._options.xrInput.xrCamera.rotationQuaternion!, this._tmpVector);
+                                this._tmpVector.addInPlace(this._options.xrInput.xrCamera.position);
+                                this._options.xrInput.xrCamera.position.subtractToRef(this._tmpVector, this._tmpVector);
+                                this._tmpRay.origin.copyFrom(this._tmpVector);
+                                this._tmpRay.direction.set(0, -1, 0);
+                                let pick = this._xrSessionManager.scene.pickWithRay(this._tmpRay, (o) => {
+                                    return this._floorMeshes.indexOf(o) !== -1;
+                                });
+
+                                // pick must exist, but stay safe
+                                if (pick && pick.pickedPoint) {
+                                    // Teleport the users feet to where they targeted
+                                    this._options.xrInput.xrCamera.position.addInPlace(pick.pickedPoint);
+                                }
+
+                            }
+                        }
+                        if (axesData.y < -0.7 && !this._currentTeleportationControllerId && !controllerData.teleportationState.rotating) {
                             controllerData.teleportationState.forward = true;
                             this._currentTeleportationControllerId = controllerData.xrController.uniqueId;
                             controllerData.teleportationState.baseRotation = this._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y;
-                            controllerData.teleportationState.currentRotation = 0;
-                            const timeToSelect = this._options.timeToTeleport || 3000;
-                            let timer = 0;
-                            const observer = this._xrSessionManager.onXRFrameObservable.add(() => {
-                                if (!mainComponent.pressed) {
-                                    this._xrSessionManager.onXRFrameObservable.remove(observer);
-                                    return;
-                                }
-                                timer += this._xrSessionManager.scene.getEngine().getDeltaTime();
-                                if (timer >= timeToSelect && this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward) {
-                                    this._teleportForward(xrController.uniqueId);
+                        }
+                        if (axesData.x) {
+                            if (!controllerData.teleportationState.forward) {
+                                if (!controllerData.teleportationState.rotating && Math.abs(axesData.x) > 0.7) {
+                                    // rotate in the right direction positive is right
+                                    controllerData.teleportationState.rotating = true;
+                                    const rotation = this.rotationAngle * (axesData.x > 0 ? 1 : -1);
+                                    this._options.xrInput.xrCamera.rotationQuaternion.multiplyInPlace(Quaternion.FromEulerAngles(0, rotation, 0));
                                 }
-
-                                // failsafe
-                                if (timer >= timeToSelect) {
-                                    this._xrSessionManager.onXRFrameObservable.remove(observer);
+                            } else {
+                                if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId) {
+                                    // set the rotation of the forward movement
+                                    if (this.rotationEnabled) {
+                                        setTimeout(() => {
+                                            controllerData.teleportationState.currentRotation = Math.atan2(axesData.x, -axesData.y);
+                                        });
+                                    } else {
+                                        controllerData.teleportationState.currentRotation = 0;
+                                    }
                                 }
-                            });
-                        } else {
-                            controllerData.teleportationState.forward = false;
-                            this._currentTeleportationControllerId = "";
-                        }
-                    }
-                });
-            } else {
-                controllerData.onButtonChangedObserver = movementController.onButtonStateChanged.add(() => {
-                    if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward && !movementController.touched) {
-                        this._teleportForward(xrController.uniqueId);
-                    }
-                });
-                // use thumbstick (or touchpad if thumbstick not available)
-                controllerData.onAxisChangedObserver = movementController.onAxisValueChanged.add((axesData) => {
-                    if (axesData.y <= 0.7 && controllerData.teleportationState.backwards) {
-                        //if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId) {
-                        controllerData.teleportationState.backwards = false;
-                        //this._currentTeleportationControllerId = "";
-                        //}
-                    }
-                    if (axesData.y > 0.7 && !controllerData.teleportationState.forward) {
-                        // teleport backwards
-                        if (!controllerData.teleportationState.backwards) {
-                            controllerData.teleportationState.backwards = true;
-                            // teleport backwards ONCE
-                            this._tmpVector.set(0, 0, -this.backwardsTeleportationDistance!);
-                            this._tmpVector.addInPlace(this._options.xrInput.xrCamera.position);
-                            this._tmpRay.origin.copyFrom(this._tmpVector);
-                            this._tmpRay.direction.set(0, -1, 0);
-                            let pick = this._xrSessionManager.scene.pickWithRay(this._tmpRay, (o) => {
-                                return this._floorMeshes.indexOf(o) !== -1;
-                            });
-
-                            // pick must exist, but stay safe
-                            if (pick && pick.pickedPoint) {
-                                // Teleport the users feet to where they targeted
-                                this._options.xrInput.xrCamera.position.addInPlace(pick.pickedPoint);
-                            }
-
-                        }
-                    }
-                    if (axesData.y < -0.7 && !this._currentTeleportationControllerId && !controllerData.teleportationState.rotating) {
-                        controllerData.teleportationState.forward = true;
-                        this._currentTeleportationControllerId = controllerData.xrController.uniqueId;
-                        controllerData.teleportationState.baseRotation = this._options.xrInput.xrCamera.rotationQuaternion.toEulerAngles().y;
-                    }
-                    if (axesData.x) {
-                        if (!controllerData.teleportationState.forward) {
-                            if (!controllerData.teleportationState.rotating && Math.abs(axesData.x) > 0.7) {
-                                // rotate in the right direction positive is right
-                                controllerData.teleportationState.rotating = true;
-                                const rotation = this.rotationAngle * (axesData.x > 0 ? 1 : -1);
-                                this._options.xrInput.xrCamera.rotationQuaternion.multiplyInPlace(Quaternion.FromEulerAngles(0, rotation, 0));
                             }
                         } else {
-                            if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId) {
-                                // set the rotation of the forward movement
-                                if (this.rotationEnabled) {
-                                    setTimeout(() => {
-                                        controllerData.teleportationState.currentRotation = Math.atan2(axesData.x, -axesData.y);
-                                    });
-                                } else {
-                                    controllerData.teleportationState.currentRotation = 0;
-                                }
-                            }
+                            controllerData.teleportationState.rotating = false;
                         }
-                    } else {
-                        controllerData.teleportationState.rotating = false;
-                    }
-                });
+                    });
+                }
             }
-        }
+        });
     }
 
     private _teleportForward(controllerId: string) {
@@ -459,7 +473,7 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
         let teleportationTarget = GroundBuilder.CreateGround("teleportationTarget", { width: 2, height: 2, subdivisions: 2 }, scene);
         teleportationTarget.isPickable = false;
         let length = 512;
-        let dynamicTexture = new DynamicTexture("DynamicTexture", length, scene, true);
+        let dynamicTexture = new DynamicTexture("teleportationPlaneDynamicTexture", length, scene, true);
         dynamicTexture.hasAlpha = true;
         let context = dynamicTexture.getContext();
         let centerX = length / 2;
@@ -474,7 +488,7 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
         context.stroke();
         context.closePath();
         dynamicTexture.update();
-        let teleportationCircleMaterial = new StandardMaterial("TextPlaneMaterial", scene);
+        const teleportationCircleMaterial = new StandardMaterial("teleportationPlaneMaterial", scene);
         teleportationCircleMaterial.diffuseTexture = dynamicTexture;
         teleportationTarget.material = teleportationCircleMaterial;
         let torus = TorusBuilder.CreateTorus("torusTeleportation", {
@@ -520,6 +534,17 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
         if (this._options.defaultTargetMeshOptions.torusArrowMaterial) {
             torus.material = this._options.defaultTargetMeshOptions.torusArrowMaterial;
             cone.material = this._options.defaultTargetMeshOptions.torusArrowMaterial;
+        } else {
+            const torusConeMaterial = new StandardMaterial("torusConsMat", scene);
+            torusConeMaterial.disableLighting = !!this._options.defaultTargetMeshOptions.disableLighting;
+            if (torusConeMaterial.disableLighting) {
+                torusConeMaterial.emissiveColor = new Color3(0.3, 0.3, 1.0);
+            } else {
+                torusConeMaterial.diffuseColor = new Color3(0.3, 0.3, 1.0);
+            }
+            torusConeMaterial.alpha = 0.9;
+            torus.material = torusConeMaterial;
+            cone.material = torusConeMaterial;
         }
 
         this._options.teleportationTargetMesh = teleportationTarget;

+ 2 - 1
src/Cameras/XR/motionController/index.ts

@@ -4,4 +4,5 @@ export * from "./webXRGenericMotionController";
 export * from "./webXRMicrosoftMixedRealityController";
 export * from "./webXRMotionControllerManager";
 export * from "./webXROculusTouchMotionController";
-export * from "./webXRHTCViveMotionController";
+export * from "./webXRHTCViveMotionController";
+export * from "./webXRProfiledMotionController";

+ 123 - 86
src/Cameras/XR/motionController/webXRAbstractController.ts

@@ -11,7 +11,7 @@ import { Mesh } from '../../../Meshes/mesh';
 /**
  * Handness type in xrInput profiles. These can be used to define layouts in the Layout Map.
  */
-export type MotionControllerHandness = "none" | "left" | "right" | "left-right" | "left-right-none";
+export type MotionControllerHandness = "none" | "left" | "right";
 /**
  * The type of components available in motion controllers.
  * This is not the name of the component.
@@ -19,6 +19,11 @@ export type MotionControllerHandness = "none" | "left" | "right" | "left-right"
 export type MotionControllerComponentType = "trigger" | "squeeze" | "touchpad" | "thumbstick" | "button";
 
 /**
+ * The state of a controller component
+ */
+export type MotionControllerComponentStateType = "default" | "touched" | "pressed";
+
+/**
  * The schema of motion controller layout.
  * No object will be initialized using this interface
  * This is used just to define the profile.
@@ -40,38 +45,76 @@ export interface IMotionControllerLayout {
              * The type of input the component outputs
              */
             type: MotionControllerComponentType;
-        }
-    };
-    /**
-     * An optional gamepad object. If no gamepad object is not defined, no models will be loaded
-     */
-    gamepad?: {
-        /**
-         * Is the mapping based on the xr-standard defined here:
-         * https://www.w3.org/TR/webxr-gamepads-module-1/#xr-standard-gamepad-mapping
-         */
-        mapping: "" | "xr-standard";
-        /**
-         * The buttons available in this input in the right order
-         * index of this button will be the index in the gamepadObject.buttons array
-         * correlates to the componentId in components
-         */
-        buttons: Array<string | null>;
-        /**
-         * Definition of the axes of the gamepad input, sorted
-         * Correlates to componentIds in the components map
-         */
-        axes: Array<{
             /**
-             * The component id that the axis correlates to
+             * The indices of this component in the gamepad object
+             */
+            gamepadIndices: {
+                /**
+                 * Index of button
+                 */
+                button?: number;
+                /**
+                 * If available, index of x-axis
+                 */
+                xAxis?: number;
+                /**
+                 * If available, index of y-axis
+                 */
+                yAxis?: number;
+            };
+            /**
+             * The mesh's root node name
              */
-            componentId: string;
+            rootNodeName: string;
             /**
-             * X or Y Axis
+             * Animation definitions for this model
              */
-            axis: "x-axis" | "y-axis";
-        } | null>;
+            visualResponses: {
+                [stateKey: string]: {
+                    /**
+                     * What property will be animated
+                     */
+                    componentProperty: "xAxis" | "yAxis" | "button" | "state";
+                    /**
+                     * What states influence this visual reponse
+                     */
+                    states: MotionControllerComponentStateType[];
+                    /**
+                     * Type of animation - movement or visibility
+                     */
+                    valueNodeProperty: "transform" | "visibility";
+                    /**
+                     * Base node name to move. Its position will be calculated according to the min and max nodes
+                     */
+                    valueNodeName?: string;
+                    /**
+                     * Minimum movement node
+                     */
+                    minNodeName?: string;
+                    /**
+                     * Max movement node
+                     */
+                    maxNodeName?: string;
+                }
+            }
+            /**
+             * If touch enabled, what is the name of node to display user feedback
+             */
+            touchPointNodeName?: string;
+        }
     };
+    /**
+     * Is it xr standard mapping or not
+     */
+    gamepadMapping: "" | "xr-standard";
+    /**
+     * Base root node of this entire model
+     */
+    rootNodeName: string;
+    /**
+     * Path to load the assets. Usually relative to the base path
+     */
+    assetPath: string;
 }
 
 /**
@@ -131,7 +174,7 @@ export interface IMotionControllerButtonMeshMap {
  * This will be expanded when touchpad animations are fully supported
  * The meshes are provided to the _lerpAxisTransform function to calculate the current position of the value mesh
  */
-export interface IMotionControllerAxisMeshMap {
+export interface IMotionControllerMeshMap {
     /**
      * The mesh that will be changed when axis value changes
      */
@@ -139,11 +182,11 @@ export interface IMotionControllerAxisMeshMap {
     /**
      * the mesh that defines the minimum value mesh position.
      */
-    minMesh: AbstractMesh;
+    minMesh?: AbstractMesh;
     /**
      * the mesh that defines the maximum value mesh position.
      */
-    maxMesh: AbstractMesh;
+    maxMesh?: AbstractMesh;
 }
 
 /**
@@ -181,17 +224,6 @@ export interface IMinimalMotionControllerObject {
 export abstract class WebXRAbstractMotionController implements IDisposable {
 
     /**
-     * Component type map
-     */
-    public static ComponentType = {
-        TRIGGER: "trigger",
-        SQUEEZE: "squeeze",
-        TOUCHPAD: "touchpad",
-        THUMBSTICK: "thumbstick",
-        BUTTON: "button"
-    };
-
-    /**
      * The profile id of this motion controller
      */
     public abstract profileId: string;
@@ -214,6 +246,11 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      */
     public rootMesh: Nullable<AbstractMesh>;
 
+    /**
+     * Disable the model's animation. Can be set at any time.
+     */
+    public disableAnimation: boolean = false;
+
     private _modelReady: boolean = false;
 
     /**
@@ -235,27 +272,23 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
         public handness: MotionControllerHandness,
         _doNotLoadControllerMesh: boolean = false) {
         // initialize the components
-        if (layout.gamepad) {
-            layout.gamepad.buttons.forEach(this._initComponent);
+        if (layout.components) {
+            Object.keys(layout.components).forEach(this._initComponent);
         }
         // Model is loaded in WebXRInput
     }
 
-    private _initComponent = (id: string | null) => {
-        if (!this.layout.gamepad || !id) { return; }
-        const type = this.layout.components[id].type;
-        const buttonIndex = this.layout.gamepad.buttons.indexOf(id);
+    private _initComponent = (id: string) => {
+        if (!id) { return; }
+        const componentDef = this.layout.components[id];
+        const type = componentDef.type;
+        const buttonIndex = componentDef.gamepadIndices.button;
         // search for axes
         let axes: number[] = [];
-        this.layout.gamepad.axes.forEach((axis, index) => {
-            if (axis && axis.componentId === id) {
-                if (axis.axis === "x-axis") {
-                    axes[0] = index;
-                } else {
-                    axes[1] = index;
-                }
-            }
-        });
+        if (componentDef.gamepadIndices.xAxis !== undefined && componentDef.gamepadIndices.yAxis !== undefined) {
+            axes.push(componentDef.gamepadIndices.xAxis, componentDef.gamepadIndices.yAxis);
+        }
+
         this.components[id] = new WebXRControllerComponent(id, type, buttonIndex, axes);
     }
 
@@ -264,7 +297,7 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      * @param xrFrame the current xr frame to use and update the model
      */
     public updateFromXRFrame(xrFrame: XRFrame): void {
-        this.getComponentTypes().forEach((id) => this.getComponent(id).update(this.gamepadObject));
+        this.getComponentIds().forEach((id) => this.getComponent(id).update(this.gamepadObject));
         this.updateModel(xrFrame);
     }
 
@@ -272,7 +305,7 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      * Get the list of components available in this motion controller
      * @returns an array of strings correlating to available components
      */
-    public getComponentTypes(): string[] {
+    public getComponentIds(): string[] {
         return Object.keys(this.components);
     }
 
@@ -294,6 +327,24 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
     }
 
     /**
+     * Get the first component of specific type
+     * @param type type of component to find
+     * @return a controller component or null if not found
+     */
+    public getComponentOfType(type: MotionControllerComponentType): Nullable<WebXRControllerComponent> {
+        return this.getAllComponentsOfType(type)[0] || null;
+    }
+
+    /**
+     * Returns all components of specific type
+     * @param type the type to search for
+     * @return an array of components with this type
+     */
+    public getAllComponentsOfType(type: MotionControllerComponentType): WebXRControllerComponent[] {
+        return this.getComponentIds().map((id) => this.components[id]).filter((component) => component.type === type);
+    }
+
+    /**
      * Loads the model correlating to this controller
      * When the mesh is loaded, the onModelLoadedObservable will be triggered
      * @returns A promise fulfilled with the result of the model loading
@@ -343,14 +394,17 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      * @param axisValue the value of the axis which determines the meshes new position
      * @hidden
      */
-    protected _lerpAxisTransform(axisMap: IMotionControllerAxisMeshMap, axisValue: number): void {
+    protected _lerpTransform(axisMap: IMotionControllerMeshMap, axisValue: number, fixValueCoordinates?: boolean): void {
+        if (!axisMap.minMesh || !axisMap.maxMesh) {
+            return;
+        }
 
         if (!axisMap.minMesh.rotationQuaternion || !axisMap.maxMesh.rotationQuaternion || !axisMap.valueMesh.rotationQuaternion) {
             return;
         }
 
         // Convert from gamepad value range (-1 to +1) to lerp range (0 to 1)
-        let lerpValue = axisValue * 0.5 + 0.5;
+        let lerpValue = fixValueCoordinates ? axisValue * 0.5 + 0.5 : axisValue;
         Quaternion.SlerpToRef(
             axisMap.minMesh.rotationQuaternion,
             axisMap.maxMesh.rotationQuaternion,
@@ -363,30 +417,13 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
             axisMap.valueMesh.position);
     }
 
-    /**
-     * Moves the buttons on the controller mesh based on their current state
-     * @param buttonName the name of the button to move
-     * @param buttonValue the value of the button which determines the buttons new position
-     */
-    protected _lerpButtonTransform(buttonMap: IMotionControllerButtonMeshMap, buttonValue: number): void {
-
-        if (!buttonMap
-            || !buttonMap.unpressedMesh.rotationQuaternion
-            || !buttonMap.pressedMesh.rotationQuaternion
-            || !buttonMap.valueMesh.rotationQuaternion) {
-            return;
-        }
-
-        Quaternion.SlerpToRef(
-            buttonMap.unpressedMesh.rotationQuaternion,
-            buttonMap.pressedMesh.rotationQuaternion,
-            buttonValue,
-            buttonMap.valueMesh.rotationQuaternion);
-        Vector3.LerpToRef(
-            buttonMap.unpressedMesh.position,
-            buttonMap.pressedMesh.position,
-            buttonValue,
-            buttonMap.valueMesh.position);
+    // 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];
+    }
+    // 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];
     }
 
     private _getGenericFilenameAndPath(): { filename: string, path: string } {
@@ -442,7 +479,7 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      * Dispose this controller, the model mesh and all its components
      */
     public dispose(): void {
-        this.getComponentTypes().forEach((id) => this.getComponent(id).dispose());
+        this.getComponentIds().forEach((id) => this.getComponent(id).dispose());
         if (this.rootMesh) {
             this.rootMesh.dispose();
         }

+ 22 - 4
src/Cameras/XR/motionController/webXRControllerComponent.ts

@@ -59,19 +59,23 @@ export class WebXRControllerComponent implements IDisposable {
     /**
      * Thumbstick component type
      */
-    public static THUMBSTICK = "xr-standard-thumbstick";
+    public static THUMBSTICK: MotionControllerComponentType = "thumbstick";
     /**
      * Touchpad component type
      */
-    public static TOUCHPAD = "xr-standard-touchpad";
+    public static TOUCHPAD: MotionControllerComponentType = "touchpad";
     /**
      * trigger component type
      */
-    public static TRIGGER = "xr-standard-trigger";
+    public static TRIGGER: MotionControllerComponentType = "trigger";
     /**
      * squeeze component type
      */
-    public static SQUEEZE = "xr-standard-squeeze";
+    public static SQUEEZE: MotionControllerComponentType = "squeeze";
+    /**
+     * button component type
+     */
+    public static BUTTON: MotionControllerComponentType = "button";
     /**
      * Observers registered here will be triggered when the state of a button changes
      * State change is either pressed / touched / value
@@ -91,6 +95,13 @@ export class WebXRControllerComponent implements IDisposable {
         y: 0
     };
     private _changes: IWebXRMotionControllerComponentChanges = {};
+    private _hasChanges: boolean = false;
+    /**
+     * Return whether or not the component changed the last frame
+     */
+    public get hasChanges(): boolean {
+        return this._hasChanges;
+    }
 
     /**
      * Creates a new component for a motion controller.
@@ -173,10 +184,15 @@ export class WebXRControllerComponent implements IDisposable {
     public update(nativeController: IMinimalMotionControllerObject) {
         let buttonUpdated = false;
         let axesUpdate = false;
+        this._hasChanges = false;
         this._changes = {};
 
         if (this.isButton()) {
             const button = nativeController.buttons[this._buttonIndex];
+            // defensive, in case a profile was forced
+            if (!button) {
+                return;
+            }
             if (this._currentValue !== button.value) {
                 this.changes.value = {
                     current: button.value,
@@ -240,9 +256,11 @@ export class WebXRControllerComponent implements IDisposable {
         }
 
         if (buttonUpdated) {
+            this._hasChanges = true;
             this.onButtonStateChanged.notifyObservers(this);
         }
         if (axesUpdate) {
+            this._hasChanges = true;
             this.onAxisValueChanged.notifyObservers(this._axes);
         }
     }

+ 54 - 23
src/Cameras/XR/motionController/webXRGenericMotionController.ts

@@ -9,27 +9,6 @@ import { Scene } from '../../../scene';
 import { Mesh } from '../../../Meshes/mesh';
 import { Quaternion } from '../../../Maths/math.vector';
 
-// https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/generic/generic-trigger-touchpad-thumbstick.json
-const GenericTriggerLayout: IMotionControllerLayoutMap = {
-    "left-right-none": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" }
-        },
-        "gamepad": {
-            "mapping": "xr-standard",
-            "buttons": [
-                "xr-standard-trigger"
-            ],
-            "axes": []
-        }
-    }
-
-};
-
-// TODO support all generic models with xr-standard mapping at:
-// https://github.com/immersive-web/webxr-input-profiles/tree/master/packages/registry/profiles/generic
-
 /**
  * A generic trigger-only motion controller for WebXR
  */
@@ -42,7 +21,7 @@ export class WebXRGenericTriggerMotionController extends WebXRAbstractMotionCont
     public profileId = WebXRGenericTriggerMotionController.ProfileId;
 
     constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness) {
-        super(scene, GenericTriggerLayout["left-right-none"], gamepadObject, handness);
+        super(scene, GenericTriggerLayout[handness], gamepadObject, handness);
     }
 
     protected _processLoadedModel(meshes: AbstractMesh[]): void {
@@ -77,4 +56,56 @@ export class WebXRGenericTriggerMotionController extends WebXRAbstractMotionCont
         return true;
     }
 
-}
+}
+
+// https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/generic/generic-trigger-touchpad-thumbstick.json
+const GenericTriggerLayout: IMotionControllerLayoutMap = {
+    "left": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {}
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "generic-trigger-left",
+        "assetPath": "left.glb"
+    },
+    "right": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {}
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "generic-trigger-right",
+        "assetPath": "right.glb"
+    },
+    "none": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {}
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "generic-trigger-none",
+        "assetPath": "none.glb"
+    }
+};

+ 168 - 69
src/Cameras/XR/motionController/webXRHTCViveMotionController.ts

@@ -10,57 +10,6 @@ import { Mesh } from '../../../Meshes/mesh';
 import { Quaternion } from '../../../Maths/math.vector';
 import { WebXRMotionControllerManager } from './webXRMotionControllerManager';
 
-const HTCViveLayout: IMotionControllerLayoutMap = {
-    "left-right-none": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-touchpad": { "type": "touchpad" },
-            "menu": { "type": "button" }
-        },
-        "gamepad": {
-            "mapping": "xr-standard",
-            "buttons": [
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                "xr-standard-touchpad",
-                null,
-                "menu"
-            ],
-            "axes": [
-                { "componentId": "xr-standard-touchpad", "axis": "x-axis" },
-                { "componentId": "xr-standard-touchpad", "axis": "y-axis" }
-            ]
-        }
-    }
-};
-
-const HTCViveLegacyLayout: IMotionControllerLayoutMap = {
-    "left-right-none": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-touchpad": { "type": "touchpad" },
-            "menu": { "type": "button" }
-        },
-        "gamepad": {
-            "mapping": "",
-            "buttons": [
-                "xr-standard-touchpad",
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                "menu"
-            ],
-            "axes": [
-                { "componentId": "xr-standard-touchpad", "axis": "x-axis" },
-                { "componentId": "xr-standard-touchpad", "axis": "y-axis" }
-            ]
-        }
-    }
-};
-
 /**
  * The motion controller class for the standard HTC-Vive controllers
  */
@@ -78,22 +27,27 @@ export class WebXRHTCViveMotionController extends WebXRAbstractMotionController
 
     private _modelRootNode: AbstractMesh;
 
+    /**
+     * Create a new Vive motion controller object
+     * @param scene the scene to use to create this controller
+     * @param gamepadObject the corresponding gamepad object
+     * @param handness the handness of the controller
+     */
     constructor(scene: Scene,
         gamepadObject: IMinimalMotionControllerObject,
-        handness: MotionControllerHandness,
-        legacyMapping: boolean = false) {
-        super(scene, legacyMapping ? HTCViveLegacyLayout["left-right-none"] : HTCViveLayout["left-right-none"], gamepadObject, handness);
+        handness: MotionControllerHandness) {
+        super(scene, HTCViveLayout[handness], gamepadObject, handness);
     }
 
     protected _processLoadedModel(_meshes: AbstractMesh[]): void {
-        this.layout.gamepad!.buttons.forEach((buttonName) => {
-            const comp = buttonName && this.getComponent(buttonName);
+        this.getComponentIds().forEach((id) => {
+            const comp = id && this.getComponent(id);
             if (comp) {
                 comp.onButtonStateChanged.add((component) => {
 
-                    if (!this.rootMesh) { return; }
+                    if (!this.rootMesh || this.disableAnimation) { return; }
 
-                    switch (buttonName) {
+                    switch (id) {
                         case "xr-standard-trigger":
                             (<AbstractMesh>(this._modelRootNode.getChildren()[6])).rotation.x = -component.value * 0.15;
                             return;
@@ -101,14 +55,6 @@ export class WebXRHTCViveMotionController extends WebXRAbstractMotionController
                             return;
                         case "xr-standard-squeeze":
                             return;
-                        case "menu":
-                            if (component.pressed) {
-                                (<AbstractMesh>(this._modelRootNode.getChildren()[2])).position.y = -0.001;
-                            }
-                            else {
-                                (<AbstractMesh>(this._modelRootNode.getChildren()[2])).position.y = 0;
-                            }
-                            return;
                     }
                 }, undefined, true);
             }
@@ -149,6 +95,159 @@ WebXRMotionControllerManager.RegisterController("htc-vive", (xrInput: XRInputSou
     return new WebXRHTCViveMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness);
 });
 
-WebXRMotionControllerManager.RegisterController("htc-vive-legacy", (xrInput: XRInputSource, scene: Scene) => {
-    return new WebXRHTCViveMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness, true);
-});
+// WebXRMotionControllerManager.RegisterController("htc-vive-legacy", (xrInput: XRInputSource, scene: Scene) => {
+//     return new WebXRHTCViveMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness, true);
+// });
+
+const HTCViveLayout: IMotionControllerLayoutMap = {
+    "left": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-touchpad": {
+                "type": "touchpad",
+                "gamepadIndices": {
+                    "button": 2,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_touchpad",
+                "visualResponses": {
+
+                },
+            },
+            "menu": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "menu",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "htc_vive_none",
+        "assetPath": "none.glb"
+    },
+    "right": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-touchpad": {
+                "type": "touchpad",
+                "gamepadIndices": {
+                    "button": 2,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_touchpad",
+                "visualResponses": {
+
+                },
+            },
+            "menu": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "menu",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "htc_vive_none",
+        "assetPath": "none.glb"
+    },
+    "none": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-touchpad": {
+                "type": "touchpad",
+                "gamepadIndices": {
+                    "button": 2,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_touchpad",
+                "visualResponses": {
+
+                },
+            },
+            "menu": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "menu",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "htc-vive-none",
+        "assetPath": "none.glb"
+    }
+};

+ 402 - 75
src/Cameras/XR/motionController/webXRMicrosoftMixedRealityController.ts

@@ -7,38 +7,10 @@ import {
 import { WebXRMotionControllerManager } from './webXRMotionControllerManager';
 import { AbstractMesh } from '../../../Meshes/abstractMesh';
 import { Scene } from '../../../scene';
-import { Logger } from '../../../Misc/logger';
 import { Mesh } from '../../../Meshes/mesh';
 import { Quaternion } from '../../../Maths/math.vector';
 import { SceneLoader } from '../../../Loading/sceneLoader';
-
-// https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/microsoft/microsoft-mixed-reality.json
-const MixedRealityProfile: IMotionControllerLayoutMap = {
-    "left-right": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-touchpad": { "type": "touchpad" },
-            "xr-standard-thumbstick": { "type": "thumbstick" }
-        },
-        "gamepad": {
-            "mapping": "xr-standard",
-            "buttons": [
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                "xr-standard-touchpad",
-                "xr-standard-thumbstick"
-            ],
-            "axes": [
-                { "componentId": "xr-standard-touchpad", "axis": "x-axis" },
-                { "componentId": "xr-standard-touchpad", "axis": "y-axis" },
-                { "componentId": "xr-standard-thumbstick", "axis": "x-axis" },
-                { "componentId": "xr-standard-thumbstick", "axis": "y-axis" }
-            ]
-        }
-    }
-};
+import { Logger } from '../../../Misc/logger';
 
 /**
  * The motion controller class for all microsoft mixed reality controllers
@@ -121,20 +93,22 @@ export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionCon
         if (!this.rootMesh) { return; }
 
         // Button Meshes
-        for (let i = 0; i < this.layout.gamepad!.buttons.length; i++) {
-            const buttonName = this.layout.gamepad!.buttons[i];
-            if (buttonName) {
-                const buttonMap = (<any>this._mapping.buttons)[buttonName];
+        this.getComponentIds().forEach((id, i) => {
+            if (this.disableAnimation) {
+                return;
+            }
+            if (id && this.rootMesh) {
+                const buttonMap = (<any>this._mapping.buttons)[id];
                 const buttonMeshName = buttonMap.rootNodeName;
                 if (!buttonMeshName) {
-                    Logger.Log('Skipping unknown button at index: ' + i + ' with mapped name: ' + buttonName);
-                    continue;
+                    Logger.Log('Skipping unknown button at index: ' + i + ' with mapped name: ' + id);
+                    return;
                 }
 
                 var buttonMesh = this._getChildByName(this.rootMesh, buttonMeshName);
                 if (!buttonMesh) {
                     Logger.Warn('Missing button mesh with name: ' + buttonMeshName);
-                    continue;
+                    return;
                 }
 
                 buttonMap.valueMesh = this._getImmediateChildByName(buttonMesh, this._mapping.defaultButton.valueNodeName);
@@ -142,10 +116,10 @@ export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionCon
                 buttonMap.unpressedMesh = this._getImmediateChildByName(buttonMesh, this._mapping.defaultButton.unpressedNodeName);
 
                 if (buttonMap.valueMesh && buttonMap.pressedMesh && buttonMap.unpressedMesh) {
-                    const comp = this.getComponent(buttonName);
+                    const comp = this.getComponent(id);
                     if (comp) {
                         comp.onButtonStateChanged.add((component) => {
-                            this._lerpButtonTransform(buttonMap, component.value);
+                            this._lerpTransform(buttonMap, component.value);
                         }, undefined, true);
                     }
                 } else {
@@ -154,51 +128,44 @@ export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionCon
                 }
             }
 
-        }
+        });
 
         // Axis Meshes
-        for (let i = 0; i < this.layout.gamepad!.axes.length; ++i) {
-            const axisData = this.layout.gamepad!.axes[i];
-            if (!axisData) {
-                Logger.Log('Skipping unknown axis at index: ' + i);
-                continue;
+        this.getComponentIds().forEach((id, i) => {
+            const comp = this.getComponent(id);
+            if (!comp.isAxes()) {
+                return;
             }
 
-            const axisMap = (<any>this._mapping.axes)[axisData.componentId][axisData.axis];
+            ["x-axis", "y-axis"].forEach((axis) => {
+                if (!this.rootMesh) { return; }
+                const axisMap = (<any>this._mapping.axes)[id][axis];
 
-            var axisMesh = this._getChildByName(this.rootMesh, axisMap.rootNodeName);
-            if (!axisMesh) {
-                Logger.Warn('Missing axis mesh with name: ' + axisMap.rootNodeName);
-                continue;
-            }
+                var axisMesh = this._getChildByName(this.rootMesh, axisMap.rootNodeName);
+                if (!axisMesh) {
+                    Logger.Warn('Missing axis mesh with name: ' + axisMap.rootNodeName);
+                    return;
+                }
 
-            axisMap.valueMesh = this._getImmediateChildByName(axisMesh, this._mapping.defaultAxis.valueNodeName);
-            axisMap.minMesh = this._getImmediateChildByName(axisMesh, this._mapping.defaultAxis.minNodeName);
-            axisMap.maxMesh = this._getImmediateChildByName(axisMesh, this._mapping.defaultAxis.maxNodeName);
+                axisMap.valueMesh = this._getImmediateChildByName(axisMesh, this._mapping.defaultAxis.valueNodeName);
+                axisMap.minMesh = this._getImmediateChildByName(axisMesh, this._mapping.defaultAxis.minNodeName);
+                axisMap.maxMesh = this._getImmediateChildByName(axisMesh, this._mapping.defaultAxis.maxNodeName);
 
-            if (axisMap.valueMesh && axisMap.minMesh && axisMap.maxMesh) {
-                const comp = this.getComponent(axisData.componentId);
-                if (comp) {
-                    comp.onAxisValueChanged.add((axisValues) => {
-                        const value = axisData.axis === "x-axis" ? axisValues.x : axisValues.y;
-                        this._lerpAxisTransform(axisMap, value);
-                    }, undefined, true);
-                }
+                if (axisMap.valueMesh && axisMap.minMesh && axisMap.maxMesh) {
+                    if (comp) {
+                        comp.onAxisValueChanged.add((axisValues) => {
+                            const value = axis === "x-axis" ? axisValues.x : axisValues.y;
+                            this._lerpTransform(axisMap, value, true);
+                        }, undefined, true);
+                    }
 
-            } else {
-                // If we didn't find the mesh, it simply means this button won't have transforms applied as mapped button value changes.
-                Logger.Warn('Missing axis submesh under mesh with name: ' + axisMap.rootNodeName);
-            }
-        }
-    }
+                } else {
+                    // If we didn't find the mesh, it simply means this button won't have transforms applied as mapped button value changes.
+                    Logger.Warn('Missing axis submesh under mesh with name: ' + axisMap.rootNodeName);
+                }
 
-    // Look through all children recursively. This will return null if no mesh exists with the given name.
-    private _getChildByName(node: AbstractMesh, name: string): AbstractMesh {
-        return <AbstractMesh>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.
-    private _getImmediateChildByName(node: AbstractMesh, name: string): AbstractMesh {
-        return <AbstractMesh>node.getChildren((n) => n.name == name, true)[0];
+            });
+        });
     }
 
     protected _getFilenameAndPath(): { filename: string; path: string; } {
@@ -254,4 +221,364 @@ export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionCon
 // register the profile
 WebXRMotionControllerManager.RegisterController("windows-mixed-reality", (xrInput: XRInputSource, scene: Scene) => {
     return new WebXRMicrosoftMixedRealityController(scene, <any>(xrInput.gamepad), xrInput.handedness);
-});
+});
+
+// https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/microsoft/microsoft-mixed-reality.json
+const MixedRealityProfile: IMotionControllerLayoutMap = {
+    "left": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+                    "xr_standard_trigger_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_trigger_pressed_value",
+                        "minNodeName": "xr_standard_trigger_pressed_min",
+                        "maxNodeName": "xr_standard_trigger_pressed_max"
+                    }
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+                    "xr_standard_squeeze_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_squeeze_pressed_value",
+                        "minNodeName": "xr_standard_squeeze_pressed_min",
+                        "maxNodeName": "xr_standard_squeeze_pressed_max"
+                    }
+                }
+            },
+            "xr-standard-touchpad": {
+                "type": "touchpad",
+                "gamepadIndices": {
+                    "button": 2,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_touchpad",
+                "visualResponses": {
+                    "xr_standard_touchpad_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_pressed_value",
+                        "minNodeName": "xr_standard_touchpad_pressed_min",
+                        "maxNodeName": "xr_standard_touchpad_pressed_max"
+                    },
+                    "xr_standard_touchpad_xaxis_pressed": {
+                        "componentProperty": "xAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_xaxis_pressed_value",
+                        "minNodeName": "xr_standard_touchpad_xaxis_pressed_min",
+                        "maxNodeName": "xr_standard_touchpad_xaxis_pressed_max"
+                    },
+                    "xr_standard_touchpad_yaxis_pressed": {
+                        "componentProperty": "yAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_yaxis_pressed_value",
+                        "minNodeName": "xr_standard_touchpad_yaxis_pressed_min",
+                        "maxNodeName": "xr_standard_touchpad_yaxis_pressed_max"
+                    },
+                    "xr_standard_touchpad_xaxis_touched": {
+                        "componentProperty": "xAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_xaxis_touched_value",
+                        "minNodeName": "xr_standard_touchpad_xaxis_touched_min",
+                        "maxNodeName": "xr_standard_touchpad_xaxis_touched_max"
+                    },
+                    "xr_standard_touchpad_yaxis_touched": {
+                        "componentProperty": "yAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_yaxis_touched_value",
+                        "minNodeName": "xr_standard_touchpad_yaxis_touched_min",
+                        "maxNodeName": "xr_standard_touchpad_yaxis_touched_max"
+                    },
+                    "xr_standard_touchpad_axes_touched": {
+                        "componentProperty": "state",
+                        "states": [
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "visibility",
+                        "valueNodeName": "xr_standard_touchpad_axes_touched_value"
+                    }
+                },
+                "touchPointNodeName": "xr_standard_touchpad_axes_touched_value"
+            },
+            "xr-standard-thumbstick": {
+                "type": "thumbstick",
+                "gamepadIndices": {
+                    "button": 3,
+                    "xAxis": 2,
+                    "yAxis": 3
+                },
+                "rootNodeName": "xr_standard_thumbstick",
+                "visualResponses": {
+                    "xr_standard_thumbstick_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_thumbstick_pressed_value",
+                        "minNodeName": "xr_standard_thumbstick_pressed_min",
+                        "maxNodeName": "xr_standard_thumbstick_pressed_max"
+                    },
+                    "xr_standard_thumbstick_xaxis_pressed": {
+                        "componentProperty": "xAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_thumbstick_xaxis_pressed_value",
+                        "minNodeName": "xr_standard_thumbstick_xaxis_pressed_min",
+                        "maxNodeName": "xr_standard_thumbstick_xaxis_pressed_max"
+                    },
+                    "xr_standard_thumbstick_yaxis_pressed": {
+                        "componentProperty": "yAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_thumbstick_yaxis_pressed_value",
+                        "minNodeName": "xr_standard_thumbstick_yaxis_pressed_min",
+                        "maxNodeName": "xr_standard_thumbstick_yaxis_pressed_max"
+                    }
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "microsoft-mixed-reality-left",
+        "assetPath": "left.glb"
+    },
+    "right": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+                    "xr_standard_trigger_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_trigger_pressed_value",
+                        "minNodeName": "xr_standard_trigger_pressed_min",
+                        "maxNodeName": "xr_standard_trigger_pressed_max"
+                    }
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+                    "xr_standard_squeeze_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_squeeze_pressed_value",
+                        "minNodeName": "xr_standard_squeeze_pressed_min",
+                        "maxNodeName": "xr_standard_squeeze_pressed_max"
+                    }
+                }
+            },
+            "xr-standard-touchpad": {
+                "type": "touchpad",
+                "gamepadIndices": {
+                    "button": 2,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_touchpad",
+                "visualResponses": {
+                    "xr_standard_touchpad_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_pressed_value",
+                        "minNodeName": "xr_standard_touchpad_pressed_min",
+                        "maxNodeName": "xr_standard_touchpad_pressed_max"
+                    },
+                    "xr_standard_touchpad_xaxis_pressed": {
+                        "componentProperty": "xAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_xaxis_pressed_value",
+                        "minNodeName": "xr_standard_touchpad_xaxis_pressed_min",
+                        "maxNodeName": "xr_standard_touchpad_xaxis_pressed_max"
+                    },
+                    "xr_standard_touchpad_yaxis_pressed": {
+                        "componentProperty": "yAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_yaxis_pressed_value",
+                        "minNodeName": "xr_standard_touchpad_yaxis_pressed_min",
+                        "maxNodeName": "xr_standard_touchpad_yaxis_pressed_max"
+                    },
+                    "xr_standard_touchpad_xaxis_touched": {
+                        "componentProperty": "xAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_xaxis_touched_value",
+                        "minNodeName": "xr_standard_touchpad_xaxis_touched_min",
+                        "maxNodeName": "xr_standard_touchpad_xaxis_touched_max"
+                    },
+                    "xr_standard_touchpad_yaxis_touched": {
+                        "componentProperty": "yAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_touchpad_yaxis_touched_value",
+                        "minNodeName": "xr_standard_touchpad_yaxis_touched_min",
+                        "maxNodeName": "xr_standard_touchpad_yaxis_touched_max"
+                    },
+                    "xr_standard_touchpad_axes_touched": {
+                        "componentProperty": "state",
+                        "states": [
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "visibility",
+                        "valueNodeName": "xr_standard_touchpad_axes_touched_value"
+                    }
+                },
+                "touchPointNodeName": "xr_standard_touchpad_axes_touched_value"
+            },
+            "xr-standard-thumbstick": {
+                "type": "thumbstick",
+                "gamepadIndices": {
+                    "button": 3,
+                    "xAxis": 2,
+                    "yAxis": 3
+                },
+                "rootNodeName": "xr_standard_thumbstick",
+                "visualResponses": {
+                    "xr_standard_thumbstick_pressed": {
+                        "componentProperty": "button",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_thumbstick_pressed_value",
+                        "minNodeName": "xr_standard_thumbstick_pressed_min",
+                        "maxNodeName": "xr_standard_thumbstick_pressed_max"
+                    },
+                    "xr_standard_thumbstick_xaxis_pressed": {
+                        "componentProperty": "xAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_thumbstick_xaxis_pressed_value",
+                        "minNodeName": "xr_standard_thumbstick_xaxis_pressed_min",
+                        "maxNodeName": "xr_standard_thumbstick_xaxis_pressed_max"
+                    },
+                    "xr_standard_thumbstick_yaxis_pressed": {
+                        "componentProperty": "yAxis",
+                        "states": [
+                            "default",
+                            "touched",
+                            "pressed"
+                        ],
+                        "valueNodeProperty": "transform",
+                        "valueNodeName": "xr_standard_thumbstick_yaxis_pressed_value",
+                        "minNodeName": "xr_standard_thumbstick_yaxis_pressed_min",
+                        "maxNodeName": "xr_standard_thumbstick_yaxis_pressed_max"
+                    }
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "microsoft-mixed-reality-right",
+        "assetPath": "right.glb"
+    }
+};

+ 124 - 28
src/Cameras/XR/motionController/webXRMotionControllerManager.ts

@@ -1,8 +1,10 @@
 import {
-    WebXRAbstractMotionController,
+    WebXRAbstractMotionController, IMotionControllerProfile,
 } from './webXRAbstractController';
 import { WebXRGenericTriggerMotionController } from './webXRGenericMotionController';
 import { Scene } from '../../../scene';
+import { Tools } from '../../../Misc/tools';
+import { WebXRProfiledMotionController } from './webXRProfiledMotionController';
 
 /**
  * A construction function type to create a new controller based on an xrInput object
@@ -18,6 +20,18 @@ export type MotionControllerConstructor = (xrInput: XRInputSource, scene: Scene)
  * When using a model try to stay as generic as possible. Eventually there will be no need in any of the controller classes
  */
 export class WebXRMotionControllerManager {
+    /**
+     * The base URL of the online controller repository. Can be changed at any time.
+     */
+    public static BaseRepositoryUrl = "https://immersive-web.github.io/webxr-input-profiles/packages/viewer/dist";
+    /**
+     * Use the online repository, or use only locally-defined controllers
+     */
+    public static UseOnlineRepository: boolean = true;
+    /**
+     * Which repository gets priority - local or online
+     */
+    public static PrioritizeOnlineRepository: boolean = true;
     private static _AvailableControllers: { [type: string]: MotionControllerConstructor } = {};
     private static _Fallbacks: { [profileId: string]: string[] } = {};
 
@@ -45,49 +59,128 @@ export class WebXRMotionControllerManager {
      * @param xrInput the xrInput to which a new controller is initialized
      * @param scene the scene to which the model will be added
      * @param forceProfile force a certain profile for this controller
-     * @return the motion controller class for this profile id or the generic standard class if none was found
+     * @return A promise that fulfils with the motion controller class for this profile id or the generic standard class if none was found
      */
-    public static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene, forceProfile?: string): WebXRAbstractMotionController {
-        //if a type was forced, try creating a controller using it. Continue if not found.
+    public static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene, forceProfile?: string): Promise<WebXRAbstractMotionController> {
+        const profileArray: string[] = [];
         if (forceProfile) {
-            const constructionFunction = this._AvailableControllers[forceProfile];
-            if (constructionFunction) {
-                return constructionFunction(xrInput, scene);
-            }
+            profileArray.push(forceProfile);
         }
+        profileArray.push(...(xrInput.profiles || []));
 
-        for (let i = 0; i < xrInput.profiles.length; ++i) {
-            const constructionFunction = this._AvailableControllers[xrInput.profiles[i]];
-            if (constructionFunction) {
-                return constructionFunction(xrInput, scene);
-            }
+        // emulator support
+        if (profileArray.length && !profileArray[0]) {
+            // remove the first "undefined" that the emulator is adding
+            profileArray.pop();
         }
-        // try using the gamepad id
+
+        // legacy support - try using the gamepad id
         if (xrInput.gamepad && xrInput.gamepad.id) {
             switch (xrInput.gamepad.id) {
                 case (xrInput.gamepad.id.match(/oculus touch/gi) ? xrInput.gamepad.id : undefined):
-                    // oculus in gamepad id - legacy mapping
-                    return this._AvailableControllers["oculus-touch-legacy"](xrInput, scene);
-                case (xrInput.gamepad.id.match(/Spatial Controller/gi) ? xrInput.gamepad.id : undefined):
-                    // oculus in gamepad id - legacy mapping
-                    return this._AvailableControllers["microsoft-mixed-reality"](xrInput, scene);
-                case (xrInput.gamepad.id.match(/openvr/gi) ? xrInput.gamepad.id : undefined):
-                    // oculus in gamepad id - legacy mapping
-                    return this._AvailableControllers["htc-vive-legacy"](xrInput, scene);
+                    // oculus in gamepad id
+                    profileArray.push("oculus-touch-v2");
+                    break;
             }
         }
+
+        // make sure microsoft/windows mixed reality works correctly
+        const windowsMRIdx = profileArray.indexOf("windows-mixed-reality");
+        if (windowsMRIdx !== -1) {
+            profileArray.splice(windowsMRIdx, 0, "microsoft-mixed-reality");
+        }
+
+        if (!profileArray.length) {
+            profileArray.push("generic-trigger");
+        }
+
+        if (this.UseOnlineRepository) {
+            const firstFunction = this.PrioritizeOnlineRepository ? this._LoadProfileFromRepository : this._LoadProfilesFromAvailableControllers;
+            const secondFunction = this.PrioritizeOnlineRepository ? this._LoadProfilesFromAvailableControllers : this._LoadProfileFromRepository;
+
+            return firstFunction.call(this, profileArray, xrInput, scene).catch(() => {
+                return secondFunction.call(this, profileArray, xrInput, scene);
+            });
+
+        } else {
+            // use only available functions
+            return this._LoadProfilesFromAvailableControllers(profileArray, xrInput, scene);
+        }
+    }
+
+    private static _LoadProfilesFromAvailableControllers(profileArray: string[], xrInput: XRInputSource, scene: Scene) {
         // check fallbacks
-        for (let i = 0; i < xrInput.profiles.length; ++i) {
-            const fallbacks = this.FindFallbackWithProfileId(xrInput.profiles[i]);
+        for (let i = 0; i < profileArray.length; ++i) {
+            // defensive
+            if (!profileArray[i]) {
+                continue;
+            }
+            const fallbacks = this.FindFallbackWithProfileId(profileArray[i]);
             for (let j = 0; j < fallbacks.length; ++j) {
                 const constructionFunction = this._AvailableControllers[fallbacks[j]];
                 if (constructionFunction) {
-                    return constructionFunction(xrInput, scene);
+                    return Promise.resolve(constructionFunction(xrInput, scene));
                 }
             }
         }
-        // return the most generic thing we have
-        return this._AvailableControllers[WebXRGenericTriggerMotionController.ProfileId](xrInput, scene);
+
+        throw new Error(`no controller requested was found in the available controllers list`);
+    }
+
+    private static _ProfilesList: Promise<{ [profile: string]: string }>;
+
+    // cache for loading
+    private static _ProfileLoadingPromises: { [profileName: string]: Promise<IMotionControllerProfile> } = {};
+
+    private static _LoadProfileFromRepository(profileArray: string[], xrInput: XRInputSource, scene: Scene): Promise<WebXRAbstractMotionController> {
+        return Promise.resolve().then(() => {
+            if (!this._ProfilesList) {
+                return this.UpdateProfilesList();
+            } else {
+                return this._ProfilesList;
+            }
+        }).then((profilesList: { [profile: string]: string }) => {
+            // load the right profile
+            for (let i = 0; i < profileArray.length; ++i) {
+                // defensive
+                if (!profileArray[i]) {
+                    continue;
+                }
+                if (profilesList[profileArray[i]]) {
+                    return profileArray[i];
+                }
+            }
+
+            throw new Error(`neither controller ${profileArray[0]} nor all fallbacks were found in the repository,`);
+        }).then((profileToLoad: string) => {
+            // load the profile
+            if (!this._ProfileLoadingPromises[profileToLoad]) {
+                this._ProfileLoadingPromises[profileToLoad] = Tools.LoadFileAsync(`${this.BaseRepositoryUrl}/profiles/${profileToLoad}/profile.json`, false).then((data) => <IMotionControllerProfile>JSON.parse(data as string));
+            }
+            return this._ProfileLoadingPromises[profileToLoad];
+        }).then((profile: IMotionControllerProfile) => {
+            return new WebXRProfiledMotionController(scene, xrInput, profile, this.BaseRepositoryUrl);
+        });
+
+    }
+
+    /**
+     * Clear the cache used for profile loading and reload when requested again
+     */
+    public static ClearProfilesCache() {
+        delete this._ProfilesList;
+        this._ProfileLoadingPromises = {};
+    }
+
+    /**
+     * Will update the list of profiles available in the repository
+     * @return a promise that resolves to a map of profiles available online
+     */
+    public static UpdateProfilesList() {
+        this._ProfilesList = Tools.LoadFileAsync(this.BaseRepositoryUrl + '/profiles/profilesList.json', false).then((data) => {
+            return JSON.parse(data.toString());
+        });
+        return this._ProfilesList;
     }
 
     /**
@@ -96,7 +189,10 @@ export class WebXRMotionControllerManager {
      * @return an array with corresponding fallback profiles
      */
     public static FindFallbackWithProfileId(profileId: string): string[] {
-        return this._Fallbacks[profileId] || [];
+        const returnArray = this._Fallbacks[profileId] || [];
+
+        returnArray.unshift(profileId);
+        return returnArray;
     }
 
     /**

+ 291 - 122
src/Cameras/XR/motionController/webXROculusTouchMotionController.ts

@@ -10,123 +10,6 @@ import { Scene } from '../../../scene';
 import { Mesh } from '../../../Meshes/mesh';
 import { Quaternion } from '../../../Maths/math.vector';
 
-// https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/microsoft/microsoft-mixed-reality.json
-const OculusTouchLayouts: IMotionControllerLayoutMap = {
-    "left": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-thumbstick": { "type": "thumbstick" },
-            "a-button": { "type": "button" },
-            "b-button": { "type": "button" },
-            "thumbrest": { "type": "button" }
-        },
-        "gamepad": {
-            "mapping": "xr-standard",
-            "buttons": [
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                null,
-                "xr-standard-thumbstick",
-                "a-button",
-                "b-button",
-                "thumbrest"
-            ],
-            "axes": [
-                null,
-                null,
-                { "componentId": "xr-standard-thumbstick", "axis": "x-axis" },
-                { "componentId": "xr-standard-thumbstick", "axis": "y-axis" }
-            ]
-        }
-    },
-    "right": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-thumbstick": { "type": "thumbstick" },
-            "x-button": { "type": "button" },
-            "y-button": { "type": "button" },
-            "thumbrest": { "type": "button" }
-        },
-        "gamepad": {
-            "mapping": "xr-standard",
-            "buttons": [
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                null,
-                "xr-standard-thumbstick",
-                "x-button",
-                "y-button",
-                "thumbrest"
-            ],
-            "axes": [
-                null,
-                null,
-                { "componentId": "xr-standard-thumbstick", "axis": "x-axis" },
-                { "componentId": "xr-standard-thumbstick", "axis": "y-axis" }
-            ]
-        }
-    }
-};
-
-const OculusTouchLegacyLayouts: IMotionControllerLayoutMap = {
-    "left": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-thumbstick": { "type": "thumbstick" },
-            "a-button": { "type": "button" },
-            "b-button": { "type": "button" },
-            "thumbrest": { "type": "button" }
-        },
-        "gamepad": {
-            "mapping": "",
-            "buttons": [
-                "xr-standard-thumbstick",
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                "a-button",
-                "b-button",
-                "thumbrest"
-            ],
-            "axes": [
-                { "componentId": "xr-standard-thumbstick", "axis": "x-axis" },
-                { "componentId": "xr-standard-thumbstick", "axis": "y-axis" }
-            ]
-        }
-    },
-    "right": {
-        "selectComponentId": "xr-standard-trigger",
-        "components": {
-            "xr-standard-trigger": { "type": "trigger" },
-            "xr-standard-squeeze": { "type": "squeeze" },
-            "xr-standard-thumbstick": { "type": "thumbstick" },
-            "x-button": { "type": "button" },
-            "y-button": { "type": "button" },
-            "thumbrest": { "type": "button" }
-        },
-        "gamepad": {
-            "mapping": "",
-            "buttons": [
-                "xr-standard-thumbstick",
-                "xr-standard-trigger",
-                "xr-standard-squeeze",
-                "x-button",
-                "y-button",
-                "thumbrest"
-            ],
-            "axes": [
-                { "componentId": "xr-standard-thumbstick", "axis": "x-axis" },
-                { "componentId": "xr-standard-thumbstick", "axis": "y-axis" }
-            ]
-        }
-    }
-};
-
 /**
  * The motion controller class for oculus touch (quest, rift).
  * This class supports legacy mapping as well the standard xr mapping
@@ -167,14 +50,14 @@ export class WebXROculusTouchMotionController extends WebXRAbstractMotionControl
         const isQuest = this._isQuest();
         const triggerDirection = this.handness === 'right' ? -1 : 1;
 
-        this.layout.gamepad!.buttons.forEach((buttonName) => {
-            const comp = buttonName && this.getComponent(buttonName);
+        this.getComponentIds().forEach((id) => {
+            const comp = id && this.getComponent(id);
             if (comp) {
                 comp.onButtonStateChanged.add((component) => {
 
-                    if (!this.rootMesh) { return; }
+                    if (!this.rootMesh || this.disableAnimation) { return; }
 
-                    switch (buttonName) {
+                    switch (id) {
                         case "xr-standard-trigger": // index trigger
                             if (!isQuest) {
                                 (<AbstractMesh>(this._modelRootNode.getChildren()[3])).rotation.x = -component.value * 0.20;
@@ -274,4 +157,290 @@ WebXRMotionControllerManager.RegisterController("oculus-touch", (xrInput: XRInpu
 
 WebXRMotionControllerManager.RegisterController("oculus-touch-legacy", (xrInput: XRInputSource, scene: Scene) => {
     return new WebXROculusTouchMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness, true);
-});
+});
+
+const OculusTouchLayouts: IMotionControllerLayoutMap = {
+    "left": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-thumbstick": {
+                "type": "thumbstick",
+                "gamepadIndices": {
+                    "button": 3,
+                    "xAxis": 2,
+                    "yAxis": 3
+                },
+                "rootNodeName": "xr_standard_thumbstick",
+                "visualResponses": {
+
+                }
+            },
+            "x-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "x_button",
+                "visualResponses": {
+
+                }
+            },
+            "y-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 5
+                },
+                "rootNodeName": "y_button",
+                "visualResponses": {
+
+                }
+            },
+            "thumbrest": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 6
+                },
+                "rootNodeName": "thumbrest",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "oculus-touch-v2-left",
+        "assetPath": "left.glb"
+    },
+    "right": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 0
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-thumbstick": {
+                "type": "thumbstick",
+                "gamepadIndices": {
+                    "button": 3,
+                    "xAxis": 2,
+                    "yAxis": 3
+                },
+                "rootNodeName": "xr_standard_thumbstick",
+                "visualResponses": {
+
+                }
+            },
+            "a-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "a_button",
+                "visualResponses": {
+
+                }
+            },
+            "b-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 5
+                },
+                "rootNodeName": "b_button",
+                "visualResponses": {
+
+                }
+            },
+            "thumbrest": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 6
+                },
+                "rootNodeName": "thumbrest",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "oculus-touch-v2-right",
+        "assetPath": "right.glb"
+    }
+};
+
+const OculusTouchLegacyLayouts: IMotionControllerLayoutMap = {
+    "left": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 2
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-thumbstick": {
+                "type": "thumbstick",
+                "gamepadIndices": {
+                    "button": 0,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_thumbstick",
+                "visualResponses": {
+
+                }
+            },
+            "x-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 3
+                },
+                "rootNodeName": "x_button",
+                "visualResponses": {
+
+                }
+            },
+            "y-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "y_button",
+                "visualResponses": {
+
+                }
+            },
+            "thumbrest": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 5
+                },
+                "rootNodeName": "thumbrest",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "oculus-touch-v2-left",
+        "assetPath": "left.glb"
+    },
+    "right": {
+        "selectComponentId": "xr-standard-trigger",
+        "components": {
+            "xr-standard-trigger": {
+                "type": "trigger",
+                "gamepadIndices": {
+                    "button": 1
+                },
+                "rootNodeName": "xr_standard_trigger",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-squeeze": {
+                "type": "squeeze",
+                "gamepadIndices": {
+                    "button": 2
+                },
+                "rootNodeName": "xr_standard_squeeze",
+                "visualResponses": {
+
+                }
+            },
+            "xr-standard-thumbstick": {
+                "type": "thumbstick",
+                "gamepadIndices": {
+                    "button": 0,
+                    "xAxis": 0,
+                    "yAxis": 1
+                },
+                "rootNodeName": "xr_standard_thumbstick",
+                "visualResponses": {
+
+                }
+            },
+            "a-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 3
+                },
+                "rootNodeName": "a_button",
+                "visualResponses": {
+
+                }
+            },
+            "b-button": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 4
+                },
+                "rootNodeName": "b_button",
+                "visualResponses": {
+
+                }
+            },
+            "thumbrest": {
+                "type": "button",
+                "gamepadIndices": {
+                    "button": 5
+                },
+                "rootNodeName": "thumbrest",
+                "visualResponses": {
+
+                }
+            }
+        },
+        "gamepadMapping": "xr-standard",
+        "rootNodeName": "oculus-touch-v2-right",
+        "assetPath": "right.glb"
+    }
+};

+ 143 - 0
src/Cameras/XR/motionController/webXRProfiledMotionController.ts

@@ -0,0 +1,143 @@
+import { AbstractMesh } from '../../../Meshes/abstractMesh';
+import { WebXRAbstractMotionController, IMotionControllerProfile, IMotionControllerMeshMap } from './webXRAbstractController';
+import { Scene } from '../../../scene';
+import { SceneLoader } from '../../../Loading/sceneLoader';
+import { Mesh } from '../../../Meshes/mesh';
+import { Axis, Space } from '../../../Maths/math.axis';
+import { Color3 } from '../../../Maths/math.color';
+import { WebXRControllerComponent } from './webXRControllerComponent';
+import { SphereBuilder } from '../../../Meshes/Builders/sphereBuilder';
+import { StandardMaterial } from '../../../Materials/standardMaterial';
+
+/**
+ * A profiled motion controller has its profile loaded from an online repository.
+ * The class is responsible of loading the model, mapping the keys and enabling model-animations
+ */
+export class WebXRProfiledMotionController extends WebXRAbstractMotionController {
+    /**
+     * The profile ID of this controller. Will be populated when the controller initializes.
+     */
+    public profileId: string;
+
+    private _buttonMeshMapping: {
+        [buttonName: string]: {
+            mainMesh: AbstractMesh;
+            states: {
+                [state: string]: IMotionControllerMeshMap
+            }
+        }
+    } = {};
+    constructor(scene: Scene, xrInput: XRInputSource, _profile: IMotionControllerProfile, private _repositoryUrl: string) {
+        super(scene, _profile.layouts[xrInput.handedness || "none"], xrInput.gamepad as any, xrInput.handedness);
+        this.profileId = _profile.profileId;
+    }
+
+    protected _getFilenameAndPath(): { filename: string; path: string; } {
+        return {
+            filename: this.layout.assetPath,
+            path: `${this._repositoryUrl}/profiles/${this.profileId}/`
+        };
+    }
+    private _touchDots: { [visKey: string]: AbstractMesh } = {};
+
+    protected _processLoadedModel(_meshes: AbstractMesh[]): void {
+        this.getComponentIds().forEach((type) => {
+            const componentInLayout = this.layout.components[type];
+            this._buttonMeshMapping[type] = {
+                mainMesh: this._getChildByName(this.rootMesh!, componentInLayout.rootNodeName),
+                states: {}
+            };
+            Object.keys(componentInLayout.visualResponses).forEach((visualResponseKey) => {
+                const visResponse = componentInLayout.visualResponses[visualResponseKey];
+                if (visResponse.valueNodeProperty === "transform") {
+                    this._buttonMeshMapping[type].states[visualResponseKey] = {
+                        valueMesh: this._getChildByName(this.rootMesh!, visResponse.valueNodeName!),
+                        minMesh: this._getChildByName(this.rootMesh!, visResponse.minNodeName!),
+                        maxMesh: this._getChildByName(this.rootMesh!, visResponse.maxNodeName!)
+                    };
+                } else {
+                    // visibility, usually for touchpads
+                    const nameOfMesh = (componentInLayout.type === WebXRControllerComponent.TOUCHPAD && componentInLayout.touchPointNodeName)
+                        ? componentInLayout.touchPointNodeName : visResponse.valueNodeName!;
+                    this._buttonMeshMapping[type].states[visualResponseKey] = {
+                        valueMesh: this._getChildByName(this.rootMesh!, nameOfMesh)
+                    };
+                    if (componentInLayout.type === WebXRControllerComponent.TOUCHPAD && !this._touchDots[visualResponseKey]) {
+                        const dot = SphereBuilder.CreateSphere(visualResponseKey + 'dot', {
+                            diameter: 0.0015,
+                            segments: 8
+                        }, this.scene);
+                        dot.material = new StandardMaterial(visualResponseKey + 'mat', this.scene);
+                        (<StandardMaterial>dot.material).diffuseColor = Color3.Red();
+                        dot.parent = this._buttonMeshMapping[type].states[visualResponseKey].valueMesh;
+                        dot.isVisible = false;
+                        this._touchDots[visualResponseKey] = dot;
+                    }
+                }
+            });
+        });
+    }
+
+    protected _setRootMesh(meshes: AbstractMesh[]): void {
+        this.rootMesh = new Mesh(this.profileId + "-" + this.handness, this.scene);
+        this.rootMesh.isPickable = false;
+        let rootMesh;
+        // Find the root node in the loaded glTF scene, and attach it as a child of 'parentMesh'
+        for (let i = 0; i < meshes.length; i++) {
+            let mesh = meshes[i];
+
+            mesh.isPickable = false;
+
+            if (!mesh.parent) {
+                // Handle root node, attach to the new parentMesh
+                rootMesh = mesh;
+            }
+        }
+
+        if (rootMesh) {
+            rootMesh.setParent(this.rootMesh);
+        }
+
+        this.rootMesh.rotate(Axis.Y, Math.PI, Space.WORLD);
+    }
+    protected _updateModel(_xrFrame: XRFrame): void {
+        if (this.disableAnimation) {
+            return;
+        }
+        this.getComponentIds().forEach((id) => {
+            const component = this.getComponent(id);
+            if (!component.hasChanges) { return; }
+            const meshes = this._buttonMeshMapping[id];
+            const componentInLayout = this.layout.components[id];
+            Object.keys(componentInLayout.visualResponses).forEach((visualResponseKey) => {
+                const visResponse = componentInLayout.visualResponses[visualResponseKey];
+                let value = component.value;
+                if (visResponse.componentProperty === "xAxis") {
+                    value = component.axes.x;
+                } else if (visResponse.componentProperty === "yAxis") {
+                    value = component.axes.y;
+                }
+                if (visResponse.valueNodeProperty === "transform") {
+                    this._lerpTransform(meshes.states[visualResponseKey], value, visResponse.componentProperty !== "button");
+                } else {
+                    // visibility
+                    meshes.states[visualResponseKey].valueMesh.isVisible = component.touched || component.pressed;
+                    if (this._touchDots[visualResponseKey]) {
+                        this._touchDots[visualResponseKey].isVisible = component.touched || component.pressed;
+                    }
+                }
+            });
+        });
+    }
+    protected _getModelLoadingConstraints(): boolean {
+        return SceneLoader.IsPluginForExtensionAvailable(".glb");
+    }
+
+    public dispose() {
+        super.dispose();
+        Object.keys(this._touchDots).forEach((visResKey) => {
+            this._touchDots[visResKey].dispose();
+        });
+    }
+
+}

+ 3 - 1
src/Cameras/XR/webXRCamera.ts

@@ -60,10 +60,12 @@ export class WebXRCamera extends FreeCamera {
 
     private _updateNumberOfRigCameras(viewCount = 1) {
         while (this.rigCameras.length < viewCount) {
-            var newCamera = new TargetCamera("view: " + this.rigCameras.length, Vector3.Zero(), this.getScene());
+            var newCamera = new TargetCamera("XR-RigCamera: " + this.rigCameras.length, Vector3.Zero(), this.getScene());
             newCamera.minZ = 0.1;
             newCamera.rotationQuaternion = new Quaternion();
             newCamera.updateUpVectorFromRotation = true;
+            newCamera.isRigCamera = true;
+            newCamera.rigParent = this;
             this.rigCameras.push(newCamera);
         }
         while (this.rigCameras.length > viewCount) {

+ 29 - 4
src/Cameras/XR/webXRController.ts

@@ -17,6 +17,17 @@ export interface IWebXRControllerOptions {
      * This can be used when creating your own profile or when testing different controllers
      */
     forceControllerProfile?: string;
+
+    /**
+     * Do not load the controller mesh, in case a different mesh needs to be loaded.
+     */
+    doNotLoadControllerMesh?: boolean;
+
+    /**
+     * Should the controller mesh be animated when a user interacts with it
+     * The pressed buttons / thumbstick and touchpad animations will be disabled
+     */
+    disableMotionControllerAnimation?: boolean;
 }
 
 /**
@@ -39,6 +50,11 @@ export class WebXRController {
     public motionController?: WebXRAbstractMotionController;
 
     /**
+     * Observers registered here will trigger when a motion controller profile was assigned to this xr controller
+     */
+    public onMotionControllerProfileLoaded = new Observable<WebXRAbstractMotionController>();
+
+    /**
      * Event that fires when the controller is removed/disposed
      */
     public onDisposeObservable = new Observable<{}>();
@@ -71,10 +87,18 @@ export class WebXRController {
 
         // for now only load motion controllers if gamepad available
         if (this.inputSource.gamepad) {
-            this.motionController = WebXRMotionControllerManager.GetMotionControllerWithXRInput(inputSource, _scene, this._options.forceControllerProfile);
-            // if the model is loaded, do your thing
-            this.motionController.onModelLoadedObservable.addOnce(() => {
-                this.motionController!.rootMesh!.parent = this.pointer;
+            WebXRMotionControllerManager.GetMotionControllerWithXRInput(inputSource, _scene, this._options.forceControllerProfile).then((motionController) => {
+                this.motionController = motionController;
+                this.onMotionControllerProfileLoaded.notifyObservers(motionController);
+                // should the model be loaded?
+                if (!this._options.doNotLoadControllerMesh) {
+                    this.motionController.loadModel().then((success) => {
+                        if (success) {
+                            this.motionController!.rootMesh!.parent = this.grip || this.pointer;
+                            this.motionController!.disableAnimation = !!this._options.disableMotionControllerAnimation;
+                        }
+                    });
+                }
             });
         }
     }
@@ -148,6 +172,7 @@ export class WebXRController {
         if (this.motionController) {
             this.motionController.dispose();
         }
+        this.onMotionControllerProfileLoaded.clear();
         this.pointer.dispose();
         this.onDisposeObservable.notifyObservers({});
     }

+ 35 - 4
src/Cameras/XR/webXRInput.ts

@@ -4,6 +4,7 @@ import { IDisposable } from "../../scene";
 import { WebXRController } from './webXRController';
 import { WebXRSessionManager } from './webXRSessionManager';
 import { WebXRCamera } from './webXRCamera';
+import { WebXRMotionControllerManager } from './motionController/webXRMotionControllerManager';
 
 /**
  * The schema for initialization options of the XR Input class
@@ -20,6 +21,23 @@ export interface IWebXRInputOptions {
      * Profiles are defined here - https://github.com/immersive-web/webxr-input-profiles/
      */
     forceInputProfile?: string;
+
+    /**
+     * Do not send a request to the controlle repository to load the profile.
+     *
+     * Instead, use the controllers available in babylon itself.
+     */
+    disableOnlineControllerRepository?: boolean;
+
+    /**
+     * A custom URL for the controllers repository
+     */
+    customControllersRepositoryURL?: string;
+
+    /**
+     * Should the controller model's components not move according to the user input
+     */
+    disableControllerAnimation?: boolean;
 }
 /**
  * XR input used to track XR inputs such as controllers/rays
@@ -73,6 +91,18 @@ export class WebXRInput implements IDisposable {
                 controller.updateFromXRFrame(frame, this.xrSessionManager.referenceSpace);
             });
         });
+
+        if (this.options.customControllersRepositoryURL) {
+            WebXRMotionControllerManager.BaseRepositoryUrl = this.options.customControllersRepositoryURL;
+        }
+
+        if (!this.options.disableOnlineControllerRepository) {
+            WebXRMotionControllerManager.UseOnlineRepository = true;
+            // pre-load the profiles list to load the controllers quicker afterwards
+            WebXRMotionControllerManager.UpdateProfilesList();
+        } else {
+            WebXRMotionControllerManager.UseOnlineRepository = false;
+        }
     }
 
     private _onInputSourcesChange = (event: XRInputSourceChangeEvent) => {
@@ -84,11 +114,12 @@ export class WebXRInput implements IDisposable {
         let sources = this.controllers.map((c) => { return c.inputSource; });
         for (let input of addInputs) {
             if (sources.indexOf(input) === -1) {
-                let controller = new WebXRController(this.xrSessionManager.scene, input, { forceControllerProfile: this.options.forceInputProfile });
+                let controller = new WebXRController(this.xrSessionManager.scene, input, {
+                    forceControllerProfile: this.options.forceInputProfile,
+                    doNotLoadControllerMesh: this.options.doNotLoadControllerMeshes,
+                    disableMotionControllerAnimation: this.options.disableControllerAnimation
+                });
                 this.controllers.push(controller);
-                if (!this.options.doNotLoadControllerMeshes && controller.motionController) {
-                    controller.motionController.loadModel();
-                }
                 this.onControllerAddedObservable.notifyObservers(controller);
             }
         }

+ 2 - 0
src/Cameras/arcRotateCamera.ts

@@ -1128,6 +1128,8 @@ export class ArcRotateCamera extends TargetCamera {
         }
         var rigCam = new ArcRotateCamera(name, this.alpha + alphaShift, this.beta, this.radius, this._target, this.getScene());
         rigCam._cameraRigParams = {};
+        rigCam.isRigCamera = true;
+        rigCam.rigParent = this;
         return rigCam;
     }
 

+ 11 - 0
src/Cameras/camera.ts

@@ -266,6 +266,17 @@ export class Camera extends Node {
      */
     public onRestoreStateObservable = new Observable<Camera>();
 
+    /**
+     * Is this camera a part of a rig system?
+     */
+    public isRigCamera: boolean = false;
+
+    /**
+     * If isRigCamera set to true this will be set with the parent camera.
+     * The parent camera is not (!) necessarily the .parent of this camera (like in the case of XR)
+     */
+    public rigParent?: Camera;
+
     /** @hidden */
     public _cameraRigParams: any;
     /** @hidden */

+ 2 - 0
src/Cameras/targetCamera.ts

@@ -432,6 +432,8 @@ export class TargetCamera extends Camera {
     public createRigCamera(name: string, cameraIndex: number): Nullable<Camera> {
         if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
             var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
+            rigCamera.isRigCamera = true;
+            rigCamera.rigParent = this;
             if (this.cameraRigMode === Camera.RIG_MODE_VR || this.cameraRigMode === Camera.RIG_MODE_WEBVR) {
                 if (!this.rotationQuaternion) {
                     this.rotationQuaternion = new Quaternion();

+ 11 - 11
src/Engines/Extensions/engine.cubeTexture.ts

@@ -76,10 +76,10 @@ declare module "../../Engines/thinEngine" {
             format: number | undefined, forcedExtension: any, createPolynomials: boolean, lodScale: number, lodOffset: number): InternalTexture;
 
         /** @hidden */
-        _partialLoadFile(url: string, index: number, loadedFiles: (string | ArrayBuffer)[], onfinish: (files: (string | ArrayBuffer)[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void>): void;
+        _partialLoadFile(url: string, index: number, loadedFiles: ArrayBuffer[], onfinish: (files: ArrayBuffer[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void>): void;
 
         /** @hidden */
-        _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>): void;
+        _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: ArrayBuffer[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>): void;
 
         /** @hidden */
         _cascadeLoadImgs(scene: Nullable<Scene>, onfinish: (images: HTMLImageElement[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void>, mimeType?: string): void;
@@ -130,9 +130,9 @@ ThinEngine.prototype._createDepthStencilCubeTexture = function(size: number, opt
     return internalTexture;
 };
 
-ThinEngine.prototype._partialLoadFile = function(url: string, index: number, loadedFiles: (string | ArrayBuffer)[],
-    onfinish: (files: (string | ArrayBuffer)[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void> = null): void {
-    var onload = (data: string | ArrayBuffer) => {
+ThinEngine.prototype._partialLoadFile = function(url: string, index: number, loadedFiles: ArrayBuffer[],
+    onfinish: (files: ArrayBuffer[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void> = null): void {
+    var onload = (data: ArrayBuffer) => {
         loadedFiles[index] = data;
         (<any>loadedFiles)._internalCount++;
 
@@ -147,11 +147,11 @@ ThinEngine.prototype._partialLoadFile = function(url: string, index: number, loa
         }
     };
 
-    this._loadFile(url, onload, undefined, undefined, true, onerror);
+    this._loadFile(url, onload as (data: string | ArrayBuffer) => void, undefined, undefined, true, onerror);
 };
 
-ThinEngine.prototype._cascadeLoadFiles = function(scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void> = null): void {
-    var loadedFiles: (string | ArrayBuffer)[] = [];
+ThinEngine.prototype._cascadeLoadFiles = function(scene: Nullable<Scene>, onfinish: (images: ArrayBuffer[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void> = null): void {
+    var loadedFiles: ArrayBuffer[] = [];
     (<any>loadedFiles)._internalCount = 0;
 
     for (let index = 0; index < 6; index++) {
@@ -262,13 +262,13 @@ ThinEngine.prototype.createCubeTexture = function(rootUrl: string, scene: Nullab
     if (loader) {
         rootUrl = loader.transformUrl(rootUrl, filteredFormat);
 
-        const onloaddata = (data: any) => {
+        const onloaddata = (data: ArrayBufferView | ArrayBufferView[]) => {
             this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
             loader!.loadCubeData(data, texture, createPolynomials, onLoad, onError);
         };
         if (files && files.length === 6) {
             if (loader.supportCascades) {
-                this._cascadeLoadFiles(scene, onloaddata, files, onError);
+                this._cascadeLoadFiles(scene, (images) => onloaddata(images.map((image) => new Uint8Array(image))), files, onError);
             }
             else {
                 if (onError) {
@@ -279,7 +279,7 @@ ThinEngine.prototype.createCubeTexture = function(rootUrl: string, scene: Nullab
             }
         }
         else {
-            this._loadFile(rootUrl, onloaddata, undefined, undefined, true, onInternalError);
+            this._loadFile(rootUrl, (data) => onloaddata(new Uint8Array(data as ArrayBuffer)), undefined, undefined, true, onInternalError);
         }
     }
     else {

+ 2 - 4
src/Engines/nativeEngine.ts

@@ -1076,9 +1076,7 @@ export class NativeEngine extends Engine {
         var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
 
         if (extension === ".env") {
-            const onloaddata = (data: any) => {
-                data = data as ArrayBuffer;
-
+            const onloaddata = (data: ArrayBufferView) => {
                 var info = EnvironmentTextureTools.GetEnvInfo(data)!;
                 texture.width = info.width;
                 texture.height = info.width;
@@ -1122,7 +1120,7 @@ export class NativeEngine extends Engine {
                     }
                 };
 
-                this._loadFile(rootUrl, onloaddata, undefined, undefined, true, onInternalError);
+                this._loadFile(rootUrl, (data) => onloaddata(new Uint8Array(data as ArrayBuffer)), undefined, undefined, true, onInternalError);
             }
         }
         else {

+ 7 - 5
src/Engines/thinEngine.ts

@@ -2874,8 +2874,8 @@ export class ThinEngine {
 
         // processing for non-image formats
         if (loader) {
-            var callback = (data: string | ArrayBuffer) => {
-                loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed) => {
+            var callback = (data: ArrayBufferView) => {
+                loader!.loadData(data, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed) => {
                     if (loadFailed) {
                         onInternalError("TextureLoader failed to load data");
                     } else {
@@ -2888,17 +2888,19 @@ export class ThinEngine {
             };
 
             if (!buffer) {
-                this._loadFile(url, callback, undefined, scene ? scene.offlineProvider : undefined, true, (request?: IWebRequest, exception?: any) => {
+                this._loadFile(url, (data) => callback(new Uint8Array(data as ArrayBuffer)), undefined, scene ? scene.offlineProvider : undefined, true, (request?: IWebRequest, exception?: any) => {
                     onInternalError("Unable to load " + (request ? request.responseURL : url, exception));
                 });
             } else {
-                //callback(buffer as ArrayBuffer);
                 if (buffer instanceof ArrayBuffer) {
+                    callback(new Uint8Array(buffer));
+                }
+                else if (ArrayBuffer.isView(buffer)) {
                     callback(buffer);
                 }
                 else {
                     if (onError) {
-                        onError("Unable to load: only ArrayBuffer supported here", null);
+                        onError("Unable to load: only ArrayBuffer or ArrayBufferView is supported", null);
                     }
                 }
             }

+ 26 - 1
src/Lights/Shadows/shadowGenerator.ts

@@ -1064,6 +1064,9 @@ export class ShadowGenerator implements IShadowGenerator {
             // Morph targets
             MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
 
+            // Clip planes
+            MaterialHelper.BindClipPlane(this._effect, scene);
+
             this._bindCustomEffectForRenderSubMeshForShadowMap(subMesh, this._effect);
 
             if (this.forceBackFacesOnly) {
@@ -1274,6 +1277,27 @@ export class ShadowGenerator implements IShadowGenerator {
             }
         }
 
+        // ClipPlanes
+        const scene = this._scene;
+        if (scene.clipPlane) {
+            defines.push("#define CLIPPLANE");
+        }
+        if (scene.clipPlane2) {
+            defines.push("#define CLIPPLANE2");
+        }
+        if (scene.clipPlane3) {
+            defines.push("#define CLIPPLANE3");
+        }
+        if (scene.clipPlane4) {
+            defines.push("#define CLIPPLANE4");
+        }
+        if (scene.clipPlane5) {
+            defines.push("#define CLIPPLANE5");
+        }
+        if (scene.clipPlane6) {
+            defines.push("#define CLIPPLANE6");
+        }
+
         // Instances
         if (useInstances) {
             defines.push("#define INSTANCES");
@@ -1298,7 +1322,8 @@ export class ShadowGenerator implements IShadowGenerator {
             this._cachedDefines = join;
 
             let shaderName = "shadowMap";
-            let uniforms = ["world", "mBones", "viewProjection", "diffuseMatrix", "lightData", "depthValues", "biasAndScale", "morphTargetInfluences", "boneTextureWidth"];
+            let uniforms = ["world", "mBones", "viewProjection", "diffuseMatrix", "lightData", "depthValues", "biasAndScale", "morphTargetInfluences", "boneTextureWidth",
+                            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6"];
             let samplers = ["diffuseSampler", "boneSampler"];
 
             // Custom shader?

+ 6 - 0
src/Materials/Node/Blocks/Input/inputBlock.ts

@@ -75,6 +75,9 @@ export class InputBlock extends NodeMaterialBlock {
                     case "Color4":
                         this._type = NodeMaterialBlockConnectionPointTypes.Color4;
                         return this._type;
+                    case "Matrix":
+                        this._type = NodeMaterialBlockConnectionPointTypes.Matrix;
+                        return this._type;
                 }
             }
 
@@ -590,6 +593,9 @@ export class InputBlock extends NodeMaterialBlock {
                 case NodeMaterialBlockConnectionPointTypes.Color4:
                     valueString = `new BABYLON.Color4(${this.value.r}, ${this.value.g}, ${this.value.b}, ${this.value.a})`;
                     break;
+                case NodeMaterialBlockConnectionPointTypes.Matrix:
+                    valueString = `BABYLON.Matrix.FromArray([${(this.value as Matrix).m.toString()}])`;
+                    break;
             }
             let finalOutput = `${variableName}.value = ${valueString};\r\n`;
             finalOutput += `${variableName}.isConstant = ${this.isConstant ? "true" : "false"};\r\n`;

+ 3 - 3
src/Materials/Textures/Loaders/basisTextureLoader.ts

@@ -56,7 +56,7 @@ export class _BasisTextureLoader implements IInternalTextureLoader {
      * @param onLoad defines the callback to trigger once the texture is ready
      * @param onError defines the callback to trigger in case of error
      */
-    public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+    public loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
         if (Array.isArray(data)) {
             return;
         }
@@ -69,7 +69,7 @@ export class _BasisTextureLoader implements IInternalTextureLoader {
                 etc2: caps.etc2 ? true : false
             }
         };
-        BasisTools.TranscodeAsync(data as ArrayBuffer, transcodeConfig).then((result) => {
+        BasisTools.TranscodeAsync(data, transcodeConfig).then((result) => {
             var hasMipmap = result.fileInfo.images[0].levels.length > 1 && texture.generateMipMaps;
             BasisTools.LoadTextureFromTranscodeResult(texture, result);
             (texture.getEngine() as Engine)._setCubeMapTextureParams(hasMipmap);
@@ -91,7 +91,7 @@ export class _BasisTextureLoader implements IInternalTextureLoader {
      * @param texture defines the BabylonJS internal texture
      * @param callback defines the method to call once ready to upload
      */
-    public loadData(data: ArrayBuffer, texture: InternalTexture,
+    public loadData(data: ArrayBufferView, texture: InternalTexture,
         callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
         var caps = texture.getEngine().getCaps();
         var transcodeConfig = {

+ 2 - 2
src/Materials/Textures/Loaders/ddsTextureLoader.ts

@@ -56,7 +56,7 @@ export class _DDSTextureLoader implements IInternalTextureLoader {
      * @param onLoad defines the callback to trigger once the texture is ready
      * @param onError defines the callback to trigger in case of error
      */
-    public loadCubeData(imgs: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+    public loadCubeData(imgs: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
         var engine = texture.getEngine() as Engine;
         var info: DDSInfo | undefined;
         var loadMipmap: boolean = false;
@@ -116,7 +116,7 @@ export class _DDSTextureLoader implements IInternalTextureLoader {
      * @param texture defines the BabylonJS internal texture
      * @param callback defines the method to call once ready to upload
      */
-    public loadData(data: ArrayBuffer, texture: InternalTexture,
+    public loadData(data: ArrayBufferView, texture: InternalTexture,
         callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
         var info = DDSTools.GetDDSInfo(data);
 

+ 2 - 3
src/Materials/Textures/Loaders/envTextureLoader.ts

@@ -55,12 +55,11 @@ export class _ENVTextureLoader implements IInternalTextureLoader {
      * @param onLoad defines the callback to trigger once the texture is ready
      * @param onError defines the callback to trigger in case of error
      */
-    public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+    public loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
         if (Array.isArray(data)) {
             return;
         }
 
-        data = data as ArrayBuffer;
         var info = EnvironmentTextureTools.GetEnvInfo(data);
         if (info) {
             texture.width = info.width;
@@ -87,7 +86,7 @@ export class _ENVTextureLoader implements IInternalTextureLoader {
      * @param texture defines the BabylonJS internal texture
      * @param callback defines the method to call once ready to upload
      */
-    public loadData(data: ArrayBuffer, texture: InternalTexture,
+    public loadData(data: ArrayBufferView, texture: InternalTexture,
         callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
         throw ".env not supported in 2d.";
     }

+ 2 - 2
src/Materials/Textures/Loaders/ktxTextureLoader.ts

@@ -65,7 +65,7 @@ export class _KTXTextureLoader implements IInternalTextureLoader {
      * @param onLoad defines the callback to trigger once the texture is ready
      * @param onError defines the callback to trigger in case of error
      */
-    public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+    public loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
         if (Array.isArray(data)) {
             return;
         }
@@ -100,7 +100,7 @@ export class _KTXTextureLoader implements IInternalTextureLoader {
      * @param texture defines the BabylonJS internal texture
      * @param callback defines the method to call once ready to upload
      */
-    public loadData(data: ArrayBuffer, texture: InternalTexture,
+    public loadData(data: ArrayBufferView, texture: InternalTexture,
         callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void {
         // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
         texture._invertVScale = !texture.invertY;

+ 5 - 5
src/Materials/Textures/Loaders/tgaTextureLoader.ts

@@ -55,7 +55,7 @@ export class _TGATextureLoader implements IInternalTextureLoader {
      * @param onLoad defines the callback to trigger once the texture is ready
      * @param onError defines the callback to trigger in case of error
      */
-    public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+    public loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
         throw ".env not supported in Cube.";
     }
 
@@ -65,13 +65,13 @@ export class _TGATextureLoader implements IInternalTextureLoader {
      * @param texture defines the BabylonJS internal texture
      * @param callback defines the method to call once ready to upload
      */
-    public loadData(data: ArrayBuffer, texture: InternalTexture,
+    public loadData(data: ArrayBufferView, texture: InternalTexture,
         callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
-        var uintData = new Uint8Array(data);
+        var bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
 
-        var header = TGATools.GetTGAHeader(uintData);
+        var header = TGATools.GetTGAHeader(bytes);
         callback(header.width, header.height, texture.generateMipMaps, false, () => {
-            TGATools.UploadContent(texture, uintData);
+            TGATools.UploadContent(texture, bytes);
         });
     }
 }

+ 2 - 2
src/Materials/Textures/internalTextureLoader.ts

@@ -45,7 +45,7 @@ export interface IInternalTextureLoader {
      * @param onLoad defines the callback to trigger once the texture is ready
      * @param onError defines the callback to trigger in case of error
      */
-    loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+    loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
 
     /**
      * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
@@ -53,6 +53,6 @@ export interface IInternalTextureLoader {
      * @param texture defines the BabylonJS internal texture
      * @param callback defines the method to call once ready to upload
      */
-    loadData(data: ArrayBuffer, texture: InternalTexture,
+    loadData(data: ArrayBufferView, texture: InternalTexture,
         callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed?: boolean) => void): void;
 }

+ 2 - 2
src/Meshes/Compression/dracoCompression.ts

@@ -280,7 +280,7 @@ export class DracoCompression implements IDisposable {
     constructor(numWorkers = DracoCompression.DefaultNumWorkers) {
         const decoder = DracoCompression.Configuration.decoder;
 
-        const decoderInfo: { url: string | undefined, wasmBinaryPromise: Promise<ArrayBuffer | undefined> } =
+        const decoderInfo: { url: string | undefined, wasmBinaryPromise: Promise<ArrayBuffer | string | undefined> } =
             (decoder.wasmUrl && decoder.wasmBinaryUrl && typeof WebAssembly === "object") ? {
                 url: decoder.wasmUrl,
                 wasmBinaryPromise: Tools.LoadFileAsync(getAbsoluteUrl(decoder.wasmBinaryUrl))
@@ -336,7 +336,7 @@ export class DracoCompression implements IDisposable {
                 }
 
                 return Tools.LoadScriptAsync(decoderInfo.url).then(() => {
-                    return createDecoderAsync(decoderWasmBinary);
+                    return createDecoderAsync(decoderWasmBinary as ArrayBuffer);
                 });
             });
         }

+ 9 - 4
src/Misc/basis.ts

@@ -151,11 +151,13 @@ export class BasisTools {
 
     /**
      * Transcodes a loaded image file to compressed pixel data
-     * @param imageData image data to transcode
+     * @param data image data to transcode
      * @param config configuration options for the transcoding
      * @returns a promise resulting in the transcoded image
      */
-    public static TranscodeAsync(imageData: ArrayBuffer, config: BasisTranscodeConfiguration): Promise<TranscodeResult> {
+    public static TranscodeAsync(data: ArrayBuffer | ArrayBufferView, config: BasisTranscodeConfiguration): Promise<TranscodeResult> {
+        const dataView = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
+
         return new Promise((res, rej) => {
             this._CreateWorkerAsync().then(() => {
                 var actionId = this._actionId++;
@@ -170,7 +172,10 @@ export class BasisTools {
                     }
                 };
                 this._Worker!.addEventListener("message", messageHandler);
-                this._Worker!.postMessage({action: "transcode", id: actionId, imageData: imageData, config: config, ignoreSupportedFormats: this._IgnoreSupportedFormats}, [imageData]);
+
+                const dataViewCopy = new Uint8Array(dataView.byteLength);
+                dataViewCopy.set(new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength));
+                this._Worker!.postMessage({action: "transcode", id: actionId, imageData: dataViewCopy, config: config, ignoreSupportedFormats: this._IgnoreSupportedFormats}, [dataViewCopy.buffer]);
             });
         });
     }
@@ -274,7 +279,7 @@ function workerFunc(): void {
             // Transcode the basis image and return the resulting pixels
             var config: BasisTranscodeConfiguration = event.data.config;
             var imgData = event.data.imageData;
-            var loadedFile = new Module.BasisFile(new Uint8Array(imgData));
+            var loadedFile = new Module.BasisFile(imgData);
             var fileInfo = GetFileInfo(loadedFile);
             var format = event.data.ignoreSupportedFormats ? null : GetSupportedTranscodeFormat(event.data.config, fileInfo);
 

+ 18 - 18
src/Misc/dds.ts

@@ -165,12 +165,12 @@ export class DDSTools {
 
     /**
      * Gets DDS information from an array buffer
-     * @param arrayBuffer defines the array buffer to read data from
+     * @param data defines the array buffer view to read data from
      * @returns the DDS information
      */
-    public static GetDDSInfo(arrayBuffer: any): DDSInfo {
-        var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
-        var extendedHeader = new Int32Array(arrayBuffer, 0, headerLengthInt + 4);
+    public static GetDDSInfo(data: ArrayBufferView): DDSInfo {
+        var header = new Int32Array(data.buffer, data.byteOffset, headerLengthInt);
+        var extendedHeader = new Int32Array(data.buffer, data.byteOffset, headerLengthInt + 4);
 
         var mipmapCount = 1;
         if (header[off_flags] & DDSD_MIPMAPCOUNT) {
@@ -445,14 +445,14 @@ export class DDSTools {
      * Uploads DDS Levels to a Babylon Texture
      * @hidden
      */
-    public static UploadDDSLevels(engine: ThinEngine, texture: InternalTexture, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex = -1, currentFace?: number) {
+    public static UploadDDSLevels(engine: ThinEngine, texture: InternalTexture, data: ArrayBufferView, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex = -1, currentFace?: number) {
         var sphericalPolynomialFaces: Nullable<Array<ArrayBufferView>> = null;
         if (info.sphericalPolynomial) {
             sphericalPolynomialFaces = new Array<ArrayBufferView>();
         }
         var ext = engine.getCaps().s3tc;
 
-        var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
+        var header = new Int32Array(data.buffer, data.byteOffset, headerLengthInt);
         var fourCC: number, width: number, height: number, dataLength: number = 0, dataOffset: number;
         var byteArray: Uint8Array, mipmapCount: number, mip: number;
         let internalCompressedFormat = 0;
@@ -558,15 +558,15 @@ export class DDSTools {
 
                         if (engine._badOS || engine._badDesktopOS || (!engine.getCaps().textureHalfFloat && !engine.getCaps().textureFloat)) { // Required because iOS has many issues with float and half float generation
                             if (bpp === 128) {
-                                floatArray = DDSTools._GetFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                                floatArray = DDSTools._GetFloatAsUIntRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
                                 if (sphericalPolynomialFaces && i == 0) {
-                                    sphericalPolynomialFaces.push(DDSTools._GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i));
+                                    sphericalPolynomialFaces.push(DDSTools._GetFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i));
                                 }
                             }
                             else if (bpp === 64) {
-                                floatArray = DDSTools._GetHalfFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                                floatArray = DDSTools._GetHalfFloatAsUIntRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
                                 if (sphericalPolynomialFaces && i == 0) {
-                                    sphericalPolynomialFaces.push(DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i));
+                                    sphericalPolynomialFaces.push(DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i));
                                 }
                             }
 
@@ -575,21 +575,21 @@ export class DDSTools {
                         else {
                             if (bpp === 128) {
                                 texture.type = Constants.TEXTURETYPE_FLOAT;
-                                floatArray = DDSTools._GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                                floatArray = DDSTools._GetFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
                                 if (sphericalPolynomialFaces && i == 0) {
                                     sphericalPolynomialFaces.push(floatArray);
                                 }
                             } else if (bpp === 64 && !engine.getCaps().textureHalfFloat) {
                                 texture.type = Constants.TEXTURETYPE_FLOAT;
-                                floatArray = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                                floatArray = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
                                 if (sphericalPolynomialFaces && i == 0) {
                                     sphericalPolynomialFaces.push(floatArray);
                                 }
                             } else { // 64
                                 texture.type = Constants.TEXTURETYPE_HALF_FLOAT;
-                                floatArray = DDSTools._GetHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                                floatArray = DDSTools._GetHalfFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
                                 if (sphericalPolynomialFaces && i == 0) {
-                                    sphericalPolynomialFaces.push(DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i));
+                                    sphericalPolynomialFaces.push(DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, data.buffer, i));
                                 }
                             }
                         }
@@ -602,12 +602,12 @@ export class DDSTools {
                         if (bpp === 24) {
                             texture.format = Constants.TEXTUREFORMAT_RGB;
                             dataLength = width * height * 3;
-                            byteArray = DDSTools._GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, rOffset, gOffset, bOffset);
+                            byteArray = DDSTools._GetRGBArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, rOffset, gOffset, bOffset);
                             engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
                         } else { // 32
                             texture.format = Constants.TEXTUREFORMAT_RGBA;
                             dataLength = width * height * 4;
-                            byteArray = DDSTools._GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, rOffset, gOffset, bOffset, aOffset);
+                            byteArray = DDSTools._GetRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, rOffset, gOffset, bOffset, aOffset);
                             engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
                         }
                     } else if (info.isLuminance) {
@@ -616,14 +616,14 @@ export class DDSTools {
                         var paddedRowSize = Math.floor((width + unpackAlignment - 1) / unpackAlignment) * unpackAlignment;
                         dataLength = paddedRowSize * (height - 1) + unpaddedRowSize;
 
-                        byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                        byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer);
                         texture.format = Constants.TEXTUREFORMAT_LUMINANCE;
                         texture.type = Constants.TEXTURETYPE_UNSIGNED_INT;
 
                         engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
                     } else {
                         dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
-                        byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
+                        byteArray = new Uint8Array(data.buffer, data.byteOffset + dataOffset, dataLength);
 
                         texture.type = Constants.TEXTURETYPE_UNSIGNED_INT;
                         engine._uploadCompressedDataToTextureDirectly(texture, internalCompressedFormat, width, height, byteArray, face, i);

+ 8 - 8
src/Misc/environmentTextureTools.ts

@@ -111,8 +111,8 @@ export class EnvironmentTextureTools {
      * @param data The array buffer containing the .env bytes.
      * @returns the environment file info (the json header) if successfully parsed.
      */
-    public static GetEnvInfo(data: ArrayBuffer): Nullable<EnvironmentTextureInfo> {
-        let dataView = new DataView(data);
+    public static GetEnvInfo(data: ArrayBufferView): Nullable<EnvironmentTextureInfo> {
+        let dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
         let pos = 0;
 
         for (let i = 0; i < EnvironmentTextureTools._MagicBytes.length; i++) {
@@ -326,11 +326,11 @@ export class EnvironmentTextureTools {
 
     /**
      * Creates the ArrayBufferViews used for initializing environment texture image data.
-     * @param arrayBuffer the underlying ArrayBuffer to which the views refer
+     * @param data the image data
      * @param info parameters that determine what views will be created for accessing the underlying buffer
      * @return the views described by info providing access to the underlying buffer
      */
-    public static CreateImageDataArrayBufferViews(arrayBuffer: any, info: EnvironmentTextureInfo): Array<Array<ArrayBufferView>> {
+    public static CreateImageDataArrayBufferViews(data: ArrayBufferView, info: EnvironmentTextureInfo): Array<Array<ArrayBufferView>> {
         if (info.version !== 1) {
             throw new Error(`Unsupported babylon environment map version "${info.version}"`);
         }
@@ -349,7 +349,7 @@ export class EnvironmentTextureTools {
             imageData[i] = new Array<ArrayBufferView>(6);
             for (let face = 0; face < 6; face++) {
                 const imageInfo = specularInfo.mipmaps[i * 6 + face];
-                imageData[i][face] = new Uint8Array(arrayBuffer, specularInfo.specularDataPosition! + imageInfo.position, imageInfo.length);
+                imageData[i][face] = new Uint8Array(data.buffer, data.byteOffset + specularInfo.specularDataPosition! + imageInfo.position, imageInfo.length);
             }
         }
 
@@ -359,11 +359,11 @@ export class EnvironmentTextureTools {
     /**
      * Uploads the texture info contained in the env file to the GPU.
      * @param texture defines the internal texture to upload to
-     * @param arrayBuffer defines the buffer cotaining the data to load
+     * @param data defines the data to load
      * @param info defines the texture info retrieved through the GetEnvInfo method
      * @returns a promise
      */
-    public static UploadEnvLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void> {
+    public static UploadEnvLevelsAsync(texture: InternalTexture, data: ArrayBufferView, info: EnvironmentTextureInfo): Promise<void> {
         if (info.version !== 1) {
             throw new Error(`Unsupported babylon environment map version "${info.version}"`);
         }
@@ -376,7 +376,7 @@ export class EnvironmentTextureTools {
 
         texture._lodGenerationScale = specularInfo.lodGenerationScale;
 
-        const imageData = EnvironmentTextureTools.CreateImageDataArrayBufferViews(arrayBuffer, info);
+        const imageData = EnvironmentTextureTools.CreateImageDataArrayBufferViews(data, info);
 
         return EnvironmentTextureTools.UploadLevelsAsync(texture, imageData);
     }

+ 6 - 6
src/Misc/khronosTextureContainer.ts

@@ -74,18 +74,18 @@ export class KhronosTextureContainer {
 
     /**
      * Creates a new KhronosTextureContainer
-     * @param arrayBuffer contents of the KTX container file
+     * @param data contents of the KTX container file
      * @param facesExpected should be either 1 or 6, based whether a cube texture or or
      * @param threeDExpected provision for indicating that data should be a 3D texture, not implemented
      * @param textureArrayExpected provision for indicating that data should be a texture array, not implemented
      */
     public constructor(
         /** contents of the KTX container file */
-        public arrayBuffer: any, facesExpected: number, threeDExpected?: boolean, textureArrayExpected?: boolean) {
+        public data: ArrayBufferView, facesExpected: number, threeDExpected?: boolean, textureArrayExpected?: boolean) {
         // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is:
         // '�', 'K', 'T', 'X', ' ', '1', '1', '�', '\r', '\n', '\x1A', '\n'
         // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
-        var identifier = new Uint8Array(this.arrayBuffer, 0, 12);
+        var identifier = new Uint8Array(this.data.buffer, this.data.byteOffset, 12);
         if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 || identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 ||
             identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D || identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
             this.isInvalid = true;
@@ -95,7 +95,7 @@ export class KhronosTextureContainer {
 
         // load the reset of the header in native 32 bit uint
         var dataSize = Uint32Array.BYTES_PER_ELEMENT;
-        var headerDataView = new DataView(this.arrayBuffer, 12, 13 * dataSize);
+        var headerDataView = new DataView(this.data.buffer, this.data.byteOffset + 12, 13 * dataSize);
         var endianness = headerDataView.getUint32(0, true);
         var littleEndian = endianness === 0x04030201;
 
@@ -166,10 +166,10 @@ export class KhronosTextureContainer {
 
         var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
         for (var level = 0; level < mipmapCount; level++) {
-            var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
+            var imageSize = new Int32Array(this.data.buffer, this.data.byteOffset + dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
             dataOffset += 4; //image data starts from next multiple of 4 offset. Each face refers to same imagesize field above.
             for (var face = 0; face < this.numberOfFaces; face++) {
-                var byteArray = new Uint8Array(this.arrayBuffer, dataOffset, imageSize);
+                var byteArray = new Uint8Array(this.data.buffer, this.data.byteOffset + dataOffset, imageSize);
 
                 const engine = texture.getEngine();
                 engine._uploadCompressedDataToTextureDirectly(texture, this.glInternalFormat, width, height, byteArray, face, level);

+ 6 - 5
src/Misc/tools.ts

@@ -350,13 +350,14 @@ export class Tools {
     /**
      * Loads a file from a url
      * @param url the file url to load
-     * @returns a promise containing an ArrayBuffer corrisponding to the loaded file
+     * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
+     * @returns a promise containing an ArrayBuffer corresponding to the loaded file
      */
-    public static LoadFileAsync(url: string): Promise<ArrayBuffer> {
+    public static LoadFileAsync(url: string, useArrayBuffer: boolean = true): Promise<ArrayBuffer | string> {
         return new Promise((resolve, reject) => {
             FileTools.LoadFile(url, (data) => {
-                resolve(data as ArrayBuffer);
-            }, undefined, undefined, true, (request, exception) => {
+                resolve(data);
+            }, undefined, undefined, useArrayBuffer, (request, exception) => {
                 reject(exception);
             });
         });
@@ -1103,7 +1104,7 @@ export class Tools {
      * Utility function to detect if the current user agent is Safari
      * @returns whether or not the current user agent is safari
      */
-    public static IsSafari() : boolean {
+    public static IsSafari(): boolean {
         return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
     }
 }

+ 7 - 3
src/Rendering/utilityLayerRenderer.ts

@@ -29,10 +29,14 @@ export class UtilityLayerRenderer implements IDisposable {
     public getRenderCamera() {
         if (this._renderCamera) {
             return this._renderCamera;
-        } else if (this.originalScene.activeCameras.length > 1) {
-            return this.originalScene.activeCameras[this.originalScene.activeCameras.length - 1];
         } else {
-            return this.originalScene.activeCamera;
+            let activeCam: Camera;
+            if (this.originalScene.activeCameras.length > 1) {
+                activeCam = this.originalScene.activeCameras[this.originalScene.activeCameras.length - 1];
+            } else {
+                activeCam = <Camera>(this.originalScene.activeCamera!);
+            }
+            return (activeCam && activeCam.isRigCamera) ? activeCam.rigParent! : activeCam;
         }
     }
     /**

+ 4 - 0
src/Shaders/shadowMap.fragment.fx

@@ -16,8 +16,12 @@ uniform vec2 depthValues;
 varying float z;
 #endif
 
+#include<clipPlaneFragmentDeclaration>
+
 void main(void)
 {
+#include<clipPlaneFragment>
+
 #ifdef ALPHATEST
     if (texture2D(diffuseSampler, vUV).a < 0.4)
         discard;

+ 5 - 0
src/Shaders/shadowMap.vertex.fx

@@ -36,6 +36,8 @@ attribute vec2 uv2;
 varying float z;
 #endif
 
+#include<clipPlaneVertexDeclaration>
+
 void main(void)
 {
 vec3 positionUpdated = position;
@@ -98,4 +100,7 @@ gl_Position = viewProjection * worldPos;
         vUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
     #endif
 #endif
+
+#include<clipPlaneVertex>
+
 }

+ 16 - 1
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -404,13 +404,28 @@ describe('Babylon Scene Loader', function() {
             const scene = new BABYLON.Scene(subject);
             const promises = new Array<Promise<void>>();
 
+            const expectedSetRequestHeaderCalls = [
+                "Range: bytes=0-19",
+                "Range: bytes=20-1399",
+                "Range: bytes=1400-1817",
+                "Range: bytes=1820-3149",
+                "Range: bytes=3152-8841",
+            ];
+
+            const setRequestHeaderCalls = new Array<string>();
+            const origSetRequestHeader = BABYLON.WebRequest.prototype.setRequestHeader;
+            sinon.stub(BABYLON.WebRequest.prototype, "setRequestHeader").callsFake(function(...args) {
+                setRequestHeaderCalls.push(args.join(": "));
+                origSetRequestHeader.apply(this, args);
+            });
+
             BABYLON.SceneLoader.OnPluginActivatedObservable.addOnce((loader: BABYLON.GLTFFileLoader) => {
                 loader.useRangeRequests = true;
                 promises.push(loader.whenCompleteAsync());
             });
 
             promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/", "LevelOfDetail.glb", scene).then(() => {
-                // do nothing
+                expect(setRequestHeaderCalls, "setRequestHeaderCalls").to.have.ordered.members(expectedSetRequestHeaderCalls);
             }));
 
             return Promise.all(promises);

BIN
tests/validation/ReferenceImages/node-material0.png


+ 1 - 1
tests/validation/config.json

@@ -8,7 +8,7 @@
         },             
         {
             "title": "Node material #0",
-            "playgroundId": "#M5VQE9#13",
+            "playgroundId": "#M5VQE9#16",
             "referenceImage": "node-material0.png",
             "renderCount": 5,
             "excludeFromAutomaticTesting": true