Raanan Weber 5 年之前
父節點
當前提交
1b595a12da

+ 209 - 14
src/Cameras/XR/motionController/webXRAbstractController.ts

@@ -8,50 +8,151 @@ import { Nullable } from '../../../types';
 import { Quaternion, Vector3 } from '../../../Maths/math.vector';
 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";
+/**
+ * The type of components available in motion controllers.
+ * This is not the name of the component.
+ */
 export type MotionControllerComponentType = "trigger" | "squeeze" | "touchpad" | "thumbstick" | "button";
 
-// Profiels can be found here - https://github.com/immersive-web/webxr-input-profiles/tree/master/packages/registry/profiles
+/**
+ * The schema of motion controller layout.
+ * No object will be initialized using this interface
+ * This is used just to define the profile.
+ */
 export interface IMotionControllerLayout {
+    /**
+     * Defines the main button component id
+     */
     selectComponentId: string;
+    /**
+     * Available components (unsorted)
+     */
     components: {
+        /**
+         * A map of component Ids
+         */
         [componentId: string]: {
+            /**
+             * 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";
-        buttons: Array<string | null>; /* correlates to the componentId in components */
+        /**
+         * 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
+             */
             componentId: string;
+            /**
+             * X or Y Axis
+             */
             axis: "x-axis" | "y-axis";
         } | null>;
     };
 }
 
+/**
+ * A definition for the layout map in the input profile
+ */
 export interface IMotionControllerLayoutMap {
+    /**
+     * Layouts with handness type as a key
+     */
     [handness: string /* handness */]: IMotionControllerLayout;
 }
+
+/**
+ * The XR Input profile schema
+ * Profiles can be found here:
+ * https://github.com/immersive-web/webxr-input-profiles/tree/master/packages/registry/profiles
+ */
 export interface IMotionControllerProfile {
+    /**
+     * The id of this profile
+     * correlates to the profile(s) in the xrInput.profiles array
+     */
     profileId: string;
+    /**
+     * fallback profiles for this profileId
+     */
     fallbackProfileIds: string[];
+    /**
+     * The layout map, with handness as key
+     */
     layouts: IMotionControllerLayoutMap;
 }
 
+/**
+ * A helper-interface for the 3 meshes needed for controller button animation
+ * The meshes are provided to the _lerpButtonTransform function to calculate the current position of the value mesh
+ */
 export interface IMotionControllerButtonMeshMap {
+    /**
+     * The mesh that will be changed when value changes
+     */
     valueMesh: AbstractMesh;
+    /**
+     * the mesh that defines the pressed value mesh position.
+     * This is used to find the max-position of this button
+     */
     pressedMesh: AbstractMesh;
+    /**
+     * the mesh that defines the unpressed value mesh position.
+     * This is used to find the min (or initial) position of this button
+     */
     unpressedMesh: AbstractMesh;
 }
 
+/**
+ * A helper-interface for the 3 meshes needed for controller axis animation.
+ * 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 {
+    /**
+     * The mesh that will be changed when axis value changes
+     */
     valueMesh: AbstractMesh;
+    /**
+     * the mesh that defines the minimum value mesh position.
+     */
     minMesh: AbstractMesh;
+    /**
+     * the mesh that defines the maximum value mesh position.
+     */
     maxMesh: AbstractMesh;
 }
 
+/**
+ * The elements needed for change-detection of the gamepad objects in motion controllers
+ */
 export interface IMinimalMotionControllerObject {
-    id?: string;
+    /**
+     * An array of available buttons
+     */
     buttons: Array<{
         /**
         * Value of the button/trigger
@@ -66,12 +167,22 @@ export interface IMinimalMotionControllerObject {
          */
         pressed: boolean;
     }>;
+    /**
+     * Available axes of this controller
+     */
     axes: number[];
 }
 
+/**
+ * An Abstract Motion controller
+ * This class receives an xrInput and a profile layout and uses those to initialize the components
+ * Each component has an observable to check for changes in value and state
+ */
 export abstract class WebXRAbstractMotionController implements IDisposable {
 
-    // constants
+    /**
+     * Component type map
+     */
     public static ComponentType = {
         TRIGGER: "trigger",
         SQUEEZE: "squeeze",
@@ -80,28 +191,57 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
         BUTTON: "button"
     };
 
+    /**
+     * The profile id of this motion controller
+     */
     public abstract profileId: string;
 
+    /**
+     * A map of components (WebXRControllerComponent) in this motion controller
+     * Components have a ComponentType and can also have both button and axis definitions
+     */
     public readonly components: {
         [id: string]: WebXRControllerComponent
     } = {};
 
+    /**
+     * Observers registered here will be triggered when the model of this controller is done loading
+     */
     public onModelLoadedObservable: Observable<WebXRAbstractMotionController> = new Observable();
+
+    /**
+     * The root mesh of the model. It is null if the model was not yet initialized
+     */
     public rootMesh: Nullable<AbstractMesh>;
+
     private _modelReady: boolean = false;
 
+    /**
+     * constructs a new abstract motion controller
+     * @param scene the scene to which the model of the controller will be added
+     * @param layout The profile layout to load
+     * @param gamepadObject The gamepad object correlating to this controller
+     * @param handness handness (left/right/none) of this controller
+     * @param _doNotLoadControllerMesh set this flag to ignore the mesh loading
+     */
     constructor(protected scene: Scene, protected layout: IMotionControllerLayout,
-        protected gamepadObject: IMinimalMotionControllerObject,
+        /**
+         * The gamepad object correlating to this controller
+         */
+        public gamepadObject: IMinimalMotionControllerObject,
+        /**
+         * handness (left/right/none) of this controller
+         */
         public handness: MotionControllerHandness,
         _doNotLoadControllerMesh: boolean = false) {
         // initialize the components
         if (layout.gamepad) {
-            layout.gamepad.buttons.forEach(this.initComponent);
+            layout.gamepad.buttons.forEach(this._initComponent);
         }
         // Model is loaded in WebXRInput
     }
 
-    private initComponent = (id: string | null) => {
+    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);
@@ -119,23 +259,45 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
         this.components[id] = new WebXRControllerComponent(id, type, buttonIndex, axes);
     }
 
-    public update(xrFrame: XRFrame) {
+    /**
+     * Update this model using the current XRFrame
+     * @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.updateModel(xrFrame);
     }
 
-    public getComponentTypes() {
+    /**
+     * Get the list of components available in this motion controller
+     * @returns an array of strings correlating to available components
+     */
+    public getComponentTypes(): string[] {
         return Object.keys(this.components);
     }
 
-    public getMainComponent() {
+    /**
+     * Get the main (Select) component of this controller as defined in the layout
+     * @returns the main component of this controller
+     */
+    public getMainComponent(): WebXRControllerComponent {
         return this.getComponent(this.layout.selectComponentId);
     }
 
-    public getComponent(id: string) {
+    /**
+     * get a component based an its component id as defined in layout.components
+     * @param id the id of the component
+     * @returns the component correlates to the id or undefined if not found
+     */
+    public getComponent(id: string): WebXRControllerComponent {
         return this.components[id];
     }
 
+    /**
+     * 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
+     */
     public async loadModel(): Promise<boolean> {
         let useGeneric = !this._getModelLoadingConstraints();
         let loadingParams = this._getGenericFilenameAndPath();
@@ -164,7 +326,11 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
         });
     }
 
-    public updateModel(xrFrame: XRFrame) {
+    /**
+     * Update the model itself with the current frame data
+     * @param xrFrame the frame to use for updating the model mesh
+     */
+    protected updateModel(xrFrame: XRFrame): void {
         if (!this._modelReady) {
             return;
         }
@@ -177,7 +343,7 @@ 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) {
+    protected _lerpAxisTransform(axisMap: IMotionControllerAxisMeshMap, axisValue: number): void {
 
         if (!axisMap.minMesh.rotationQuaternion || !axisMap.maxMesh.rotationQuaternion || !axisMap.valueMesh.rotationQuaternion) {
             return;
@@ -202,7 +368,7 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
      * @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) {
+    protected _lerpButtonTransform(buttonMap: IMotionControllerButtonMeshMap, buttonValue: number): void {
 
         if (!buttonMap
             || !buttonMap.unpressedMesh.rotationQuaternion
@@ -243,13 +409,42 @@ export abstract class WebXRAbstractMotionController implements IDisposable {
         this.rootMesh.rotationQuaternion = Quaternion.FromEulerAngles(0, Math.PI, 0);
     }
 
+    /**
+     * Get the filename and path for this controller's model
+     * @returns a map of filename and path
+     */
     protected abstract _getFilenameAndPath(): { filename: string, path: string };
+    /**
+     * This function will be called after the model was successfully loaded and can be used
+     * for mesh transformations before it is available for the user
+     * @param meshes the loaded meshes
+     */
     protected abstract _processLoadedModel(meshes: AbstractMesh[]): void;
+    /**
+     * Set the root mesh for this controller. Important for the WebXR controller class
+     * @param meshes the loaded meshes
+     */
     protected abstract _setRootMesh(meshes: AbstractMesh[]): void;
+    /**
+     * A function executed each frame that updates the mesh (if needed)
+     * @param xrFrame the current xrFrame
+     */
     protected abstract _updateModel(xrFrame: XRFrame): void;
+    /**
+     * This function is called before the mesh is loaded. It checks for loading constraints.
+     * For example, this function can check if the GLB loader is available
+     * If this function returns false, the generic controller will be loaded instead
+     * @returns Is the client ready to load the mesh
+     */
     protected abstract _getModelLoadingConstraints(): boolean;
 
+    /**
+     * Dispose this controller, the model mesh and all its components
+     */
     public dispose(): void {
         this.getComponentTypes().forEach((id) => this.getComponent(id).dispose());
+        if (this.rootMesh) {
+            this.rootMesh.dispose();
+        }
     }
 }

+ 163 - 14
src/Cameras/XR/motionController/webXRControllerComponent.ts

@@ -2,64 +2,186 @@ import { IMinimalMotionControllerObject, MotionControllerComponentType } from ".
 import { Observable } from '../../../Misc/observable';
 import { IDisposable } from '../../../scene';
 
+/**
+ * X-Y values for axes in WebXR
+ */
+export interface IWebXRMotionControllerAxesValue {
+    /**
+     * The value of the x axis
+     */
+    x: number;
+    /**
+     * The value of the y-axis
+     */
+    y: number;
+}
+
+/**
+ * changed / previous values for the values of this component
+ */
+export interface IWebXRMotionControllerComponentChangesValues<T> {
+    /**
+     * current (this frame) value
+     */
+    current: T;
+    /**
+     * previous (last change) value
+     */
+    previous: T;
+}
+
+/**
+ * Represents changes in the component between current frame and last values recorded
+ */
+export interface IWebXRMotionControllerComponentChanges {
+    /**
+     * will be populated with previous and current values if touched changed
+     */
+    touched?: IWebXRMotionControllerComponentChangesValues<boolean>;
+    /**
+     * will be populated with previous and current values if pressed changed
+     */
+    pressed?: IWebXRMotionControllerComponentChangesValues<boolean>;
+    /**
+     * will be populated with previous and current values if value changed
+     */
+    value?: IWebXRMotionControllerComponentChangesValues<number>;
+    /**
+     * will be populated with previous and current values if axes changed
+     */
+    axes?: IWebXRMotionControllerComponentChangesValues<IWebXRMotionControllerAxesValue>;
+}
+/**
+ * This class represents a single component (for example button or thumbstick) of a motion controller
+ */
 export class WebXRControllerComponent implements IDisposable {
 
+    /**
+     * Observers registered here will be triggered when the state of a button changes
+     * State change is either pressed / touched / value
+     */
     public onButtonStateChanged: Observable<WebXRControllerComponent> = new Observable();
+    /**
+     * If axes are available for this component (like a touchpad or thumbstick) the observers will be notified when
+     * the axes data changes
+     */
     public onAxisValueChanged: Observable<{ x: number, y: number }> = new Observable();
 
     private _currentValue: number = 0;
     private _touched: boolean = false;
     private _pressed: boolean = false;
-    private _axes: {
-        x: number;
-        y: number;
-    } = {
-            x: 0,
-            y: 0
-        };
-
-    constructor(public id: string,
+    private _axes: IWebXRMotionControllerAxesValue = {
+        x: 0,
+        y: 0
+    };
+    private _changes: IWebXRMotionControllerComponentChanges = {};
+
+    /**
+     * Creates a new component for a motion controller.
+     * It is created by the motion controller itself
+     *
+     * @param id the id of this component
+     * @param type the type of the component
+     * @param _buttonIndex index in the buttons array of the gamepad
+     * @param _axesIndices indices of the values in the axes array of the gamepad
+     */
+    constructor(
+        /**
+         * the id of this component
+         */
+        public id: string,
+        /**
+         * the type of the component
+         */
         public type: MotionControllerComponentType,
         private _buttonIndex: number = -1,
         private _axesIndices: number[] = []) {
 
     }
 
-    public get value() {
+    /**
+     * Get the current value of this component
+     */
+    public get value(): number {
         return this._currentValue;
     }
 
-    public get pressed() {
+    /**
+     * is the button currently pressed
+     */
+    public get pressed(): boolean {
         return this._pressed;
     }
 
-    public get touched() {
+    /**
+     * is the button currently touched
+     */
+    public get touched(): boolean {
         return this._touched;
     }
 
-    public isButton() {
+    /**
+     * The current axes data. If this component has no axes it will still return an object { x: 0, y: 0 }
+     */
+    public get axes(): IWebXRMotionControllerAxesValue {
+        return this._axes;
+    }
+
+    /**
+     * Get the changes. Elements will be populated only if they changed with their previous and current value
+     */
+    public get changes(): IWebXRMotionControllerComponentChanges {
+        return this._changes;
+    }
+
+    /**
+     * Is this component a button (hence - pressable)
+     * @returns true if can be pressed
+     */
+    public isButton(): boolean {
         return this._buttonIndex !== -1;
     }
 
-    public isAxes() {
+    /**
+     * Are there axes correlating to this component
+     * @return true is axes data is available
+     */
+    public isAxes(): boolean {
         return this._axesIndices.length !== 0;
     }
 
+    /**
+     * update this component using the gamepad object it is in. Called on every frame
+     * @param nativeController the native gamepad controller object
+     */
     public update(nativeController: IMinimalMotionControllerObject) {
         let buttonUpdated = false;
         let axesUpdate = false;
+        this._changes = {};
 
         if (this.isButton()) {
             const button = nativeController.buttons[this._buttonIndex];
             if (this._currentValue !== button.value) {
+                this.changes.value = {
+                    current: button.value,
+                    previous: this._currentValue
+                };
                 buttonUpdated = true;
                 this._currentValue = button.value;
             }
             if (this._touched !== button.touched) {
+                this.changes.touched = {
+                    current: button.touched,
+                    previous: this._touched
+                };
                 buttonUpdated = true;
                 this._touched = button.touched;
             }
             if (this._pressed !== button.pressed) {
+                this.changes.pressed = {
+                    current: button.pressed,
+                    previous: this._pressed
+                };
                 buttonUpdated = true;
                 this._pressed = button.pressed;
             }
@@ -67,11 +189,35 @@ export class WebXRControllerComponent implements IDisposable {
 
         if (this.isAxes()) {
             if (this._axes.x !== nativeController.axes[this._axesIndices[0]]) {
+                this.changes.axes = {
+                    current: {
+                        x: nativeController.axes[this._axesIndices[0]],
+                        y: this._axes.y
+                    },
+                    previous: {
+                        x: this._axes.x,
+                        y: this._axes.y
+                    }
+                };
                 this._axes.x = nativeController.axes[this._axesIndices[0]];
                 axesUpdate = true;
             }
 
             if (this._axes.y !== nativeController.axes[this._axesIndices[1]]) {
+                if (this.changes.axes) {
+                    this.changes.axes.current.y = nativeController.axes[this._axesIndices[1]];
+                } else {
+                    this.changes.axes = {
+                        current: {
+                            x: this._axes.x,
+                            y: nativeController.axes[this._axesIndices[1]]
+                        },
+                        previous: {
+                            x: this._axes.x,
+                            y: this._axes.y
+                        }
+                    };
+                }
                 this._axes.y = nativeController.axes[this._axesIndices[1]];
                 axesUpdate = true;
             }
@@ -85,6 +231,9 @@ export class WebXRControllerComponent implements IDisposable {
         }
     }
 
+    /**
+     * Dispose this component
+     */
     public dispose(): void {
         this.onAxisValueChanged.clear();
         this.onButtonStateChanged.clear();

+ 8 - 1
src/Cameras/XR/motionController/webXRGenericMotionController.ts

@@ -30,9 +30,16 @@ const GenericTriggerLayout: IMotionControllerLayoutMap = {
 // 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
+ */
 export class WebXRGenericTriggerMotionController extends WebXRAbstractMotionController {
+    /**
+     * Static version of the profile id of this controller
+     */
     public static ProfileId = "generic-trigger";
-    public profileId = "generic-trigger";
+
+    public profileId = WebXRGenericTriggerMotionController.ProfileId;
 
     constructor(scene: Scene, gamepadObject: IMinimalMotionControllerObject, handness: MotionControllerHandness) {
         super(scene, GenericTriggerLayout["left-right-none"], gamepadObject, handness);

+ 3 - 0
src/Cameras/XR/motionController/webXRHTCViveMotionController.ts

@@ -61,6 +61,9 @@ const HTCViveLegacyLayout: IMotionControllerLayoutMap = {
     }
 };
 
+/**
+ * The motion controller class for the standard HTC-Vive controllers
+ */
 export class WebXRHTCViveMotionController extends WebXRAbstractMotionController {
     /**
      * The base url used to load the left and right controller models

+ 3 - 0
src/Cameras/XR/motionController/webXRMicrosoftMixedRealityController.ts

@@ -42,6 +42,9 @@ const MixedRealityProfile: IMotionControllerLayoutMap = {
     }
 };
 
+/**
+ * The motion controller class for all microsoft mixed reality controllers
+ */
 export class WebXRMicrosoftMixedRealityController extends WebXRAbstractMotionController {
     /**
      * The base url used to load the left and right controller models

+ 46 - 9
src/Cameras/XR/motionController/webXRMotionControllerManager.ts

@@ -4,6 +4,9 @@ import {
 import { WebXRGenericTriggerMotionController } from './webXRGenericMotionController';
 import { Scene } from '../../../scene';
 
+/**
+ * A construction function type to create a new controller based on an xrInput object
+ */
 export type MotionControllerConstructor = (xrInput: XRInputSource, scene: Scene) => WebXRAbstractMotionController;
 
 /**
@@ -17,11 +20,33 @@ export type MotionControllerConstructor = (xrInput: XRInputSource, scene: Scene)
 export class WebXRMotionControllerManager {
     private static _AvailableControllers: { [type: string]: MotionControllerConstructor } = {};
     private static _Fallbacks: { [profileId: string]: string[] } = {};
+
+    /**
+     * Register a new controller based on its profile. This function will be called by the controller classes themselves.
+     *
+     * If you are missing a profile, make sure it is imported in your source, otherwise it will not register.
+     *
+     * @param type the profile type to register
+     * @param constructFunction the function to be called when loading this profile
+     */
     public static RegisterController(type: string, constructFunction: MotionControllerConstructor) {
         this._AvailableControllers[type] = constructFunction;
     }
 
-    public static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene) {
+    /**
+     * When acquiring a new xrInput object (usually by the WebXRInput class), match it with the correct profile.
+     * The order of search:
+     *
+     * 1) Iterate the profiles array of the xr input and try finding a corresponding motion controller
+     * 2) (If not found) search in the gamepad id and try using it (legacy versions only)
+     * 3) search for registered fallbacks (should be redundant, nonetheless it makes sense to check)
+     * 4) return the generic trigger controller if none were found
+     *
+     * @param xrInput the xrInput to which a new controller is initialized
+     * @param scene the scene to which the model will be added
+     * @return the motion controller class for this profile id or the generic standard class if none was found
+     */
+    public static GetMotionControllerWithXRInput(xrInput: XRInputSource, scene: Scene): WebXRAbstractMotionController {
         for (let i = 0; i < xrInput.profiles.length; ++i) {
             const constructionFunction = this._AvailableControllers[xrInput.profiles[i]];
             if (constructionFunction) {
@@ -45,12 +70,10 @@ export class WebXRMotionControllerManager {
         // check fallbacks
         for (let i = 0; i < xrInput.profiles.length; ++i) {
             const fallbacks = this.FindFallbackWithProfileId(xrInput.profiles[i]);
-            if (fallbacks) {
-                for (let j = 0; j < fallbacks.length; ++j) {
-                    const constructionFunction = this._AvailableControllers[fallbacks[j]];
-                    if (constructionFunction) {
-                        return constructionFunction(xrInput, scene);
-                    }
+            for (let j = 0; j < fallbacks.length; ++j) {
+                const constructionFunction = this._AvailableControllers[fallbacks[j]];
+                if (constructionFunction) {
+                    return constructionFunction(xrInput, scene);
                 }
             }
         }
@@ -58,11 +81,21 @@ export class WebXRMotionControllerManager {
         return this._AvailableControllers[WebXRGenericTriggerMotionController.ProfileId](xrInput, scene);
     }
 
+    /**
+     * 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
+     * @return an array with corresponding fallback profiles
+     */
     public static FindFallbackWithProfileId(profileId: string): string[] {
-        return this._Fallbacks[profileId];
+        return this._Fallbacks[profileId] || [];
     }
 
-    public static RegisterFallbacksForProfileId(profileId: string, fallbacks: string[]) {
+    /**
+     * Register a fallback to a specific profile.
+     * @param profileId the profileId that will receive the fallbacks
+     * @param fallbacks A list of fallback profiles
+     */
+    public static RegisterFallbacksForProfileId(profileId: string, fallbacks: string[]): void {
         if (this._Fallbacks[profileId]) {
             this._Fallbacks[profileId].push(...fallbacks);
         } else {
@@ -70,6 +103,10 @@ export class WebXRMotionControllerManager {
         }
     }
 
+    /**
+     * Register the default fallbacks.
+     * This function is called automatically when this file is imported.
+     */
     public static DefaultFallbacks() {
         this.RegisterFallbacksForProfileId("google-daydream", ["generic-touchpad"]);
         this.RegisterFallbacksForProfileId("htc-vive-focus", ["generic-trigger-touchpad"]);

+ 4 - 0
src/Cameras/XR/motionController/webXROculusTouchMotionController.ts

@@ -127,6 +127,10 @@ const OculusTouchLegacyLayouts: IMotionControllerLayoutMap = {
     }
 };
 
+/**
+ * The motion controller class for oculus touch (quest, rift).
+ * This class supports legacy mapping as well the standard xr mapping
+ */
 export class WebXROculusTouchMotionController extends WebXRAbstractMotionController {
     /**
      * The base url used to load the left and right controller models

+ 1 - 1
src/Cameras/XR/webXRController.ts

@@ -97,7 +97,7 @@ export class WebXRController {
         }
         if (this.gamepadController) {
             // either update buttons only or also position, if in gamepad mode
-            this.gamepadController.update(xrFrame);
+            this.gamepadController.updateFromXRFrame(xrFrame);
         }
     }
 

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

@@ -5,7 +5,13 @@ import { WebXRController } from './webXRController';
 import { WebXRSessionManager } from './webXRSessionManager';
 import { WebXRCamera } from './webXRCamera';
 
+/**
+ * The schema for initialization options of the XR Input class
+ */
 export interface IWebXRInputOptions {
+    /**
+     * If set to true no model will be automatically loaded
+     */
     doNotLoadControllerMeshes?: boolean;
 }
 /**
@@ -30,16 +36,20 @@ export class WebXRInput implements IDisposable {
 
     /**
      * Initializes the WebXRInput
-     * @param baseExperience experience helper which the input should be created for
+     * @param xrSessionManager the xr session manager for this session
+     * @param xrCamera the WebXR camera for this session. Mainly used for teleportation
+     * @param options = initialization options for this xr input
      */
     public constructor(
         /**
-         * Base experience the input listens to
+         * the xr session manager for this session
          */
         public xrSessionManager: WebXRSessionManager,
-
+        /**
+         * the WebXR camera for this session. Mainly used for teleportation
+         */
         public xrCamera: WebXRCamera,
-        private options: IWebXRInputOptions = {}
+        private readonly options: IWebXRInputOptions = {}
     ) {
         // Remove controllers when exiting XR
         this._sessionEndedObserver = this.xrSessionManager.onXRSessionEnded.add(() => {