Browse Source

code doc and api changes

Raanan Weber 5 năm trước cách đây
mục cha
commit
ed4853244b
1 tập tin đã thay đổi với 178 bổ sung88 xóa
  1. 178 88
      src/Cameras/XR/features/WebXRControllerTeleportation.ts

+ 178 - 88
src/Cameras/XR/features/WebXRControllerTeleportation.ts

@@ -23,23 +23,55 @@ import { LinesBuilder } from '../../../Meshes/Builders/linesBuilder';
 
 const Name = "xr-controller-teleportation";
 
+/**
+ * The options container for the teleportation module
+ */
 export interface IWebXRTeleportationOptions {
+    /**
+     * Babylon XR Input class for controller
+     */
     xrInput: WebXRInput;
+    /**
+     * A list of meshes to use as floor meshes.
+     * Meshes can be added and removed after initializing the feature using the
+     * addFloorMesh and removeFloorMesh functions
+     */
     floorMeshes: AbstractMesh[];
-    rotationAngle?: number;
-    backwardsTeleportationDistance?: number;
+    /**
+     * Provide your own teleportation mesh instead of babylon's wonderful doughnut.
+     * If you want to support rotation, make sure your mesh has a direction indicator.
+     *
+     * When left untouched, the default mesh will be initialized.
+     */
     teleportationTargetMesh?: AbstractMesh;
+    /**
+     * Values to configure the default target mesh
+     */
     defaultTargetMeshOptions?: {
+        /**
+         * Fill color of the teleportation area
+         */
         teleportationFillColor?: string;
+        /**
+         * Border color for the teleportation area
+         */
         teleportationBorderColor?: string;
+        /**
+         * Override the default material of the torus and arrow
+         */
         torusArrowMaterial?: Material;
+        /**
+         * Disable the mesh's animation sequence
+         */
         disableAnimation?: boolean;
     };
-    disableTeleportationParabolicRay?: boolean;
-    disableRotation?: boolean;
-    parabolicCheckRadius?: number;
 }
 
+/**
+ * This is a teleportation feature to be used with webxr-enabled motion controllers.
+ * When enabled and attached, the feature will allow a user to move aroundand rotate in the scene using
+ * the input of the attached controllers.
+ */
 export class WebXRMotionControllerTeleportation implements IWebXRFeature {
     /**
      * The module's name
@@ -52,6 +84,33 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
      */
     public static readonly Version = 1;
 
+    /**
+     * Is rotation enabled when moving forward?
+     * Disabling this feature will prevent the user from deciding the direction when teleporting
+     */
+    public rotationEnabled: boolean = true;
+    /**
+     * Should the module support parabolic ray on top of direct ray
+     * If enabled, the user will be able to point "at the sky" and move according to predefined radius distance
+     * Very helpful when moving between floors / different heights
+     */
+    public parabolicRayEnabled: boolean = true;
+    /**
+     * The distance from the user to the inspection point in the direction of the controller
+     * A higher number will allow the user to move further
+     * defaults to 5 (meters, in xr units)
+     */
+    public parabolicCheckRadius: number = 5;
+    /**
+     * How much rotation should be applied when rotating right and left
+     */
+    public rotationAngle: number = Math.PI / 8;
+
+    /**
+     * Distance to travel when moving backwards
+     */
+    public backwardsTeleportationDistance: number = 0.5;
+
     private _observerTracked: Nullable<Observer<XRFrame>>;
 
     private _attached: boolean = false;
@@ -62,24 +121,61 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         return this._attached;
     }
 
+    /**
+     * Add a new mesh to the floor meshes array
+     * @param mesh the mesh to use as floor mesh
+     */
+    public addFloorMesh(mesh: AbstractMesh) {
+        this._options.floorMeshes.push(mesh);
+    }
+
+    /**
+     * Remove a mesh from the floor meshes array
+     * @param mesh the mesh to remove
+     */
+    public removeFloorMesh(mesh: AbstractMesh) {
+        const index = this._options.floorMeshes.indexOf(mesh);
+        if (index !== -1) {
+            this._options.floorMeshes.splice(index, 1);
+        }
+    }
+
+    /**
+     * Remove a mesh from the floor meshes array using its name
+     * @param name the mesh name to remove
+     */
+    public removeFloorMeshByName(name: string) {
+        const mesh = this._xrSessionManager.scene.getMeshByName(name);
+        if (mesh) {
+            this.removeFloorMesh(mesh);
+        }
+    }
+
     private _tmpRay = new Ray(new Vector3(), new Vector3());
     private _tmpVector = new Vector3();
 
+    private _controllers: {
+        [controllerUniqueId: string]: {
+            xrController: WebXRController;
+            teleportationComponent?: WebXRControllerComponent;
+            teleportationState: {
+                forward: boolean;
+                backwards: boolean;
+                currentRotation: number;
+                baseRotation: number;
+                rotating: boolean;
+            }
+            onAxisChangedObserver?: Nullable<Observer<IWebXRMotionControllerAxesValue>>;
+            onButtonChangedObserver?: Nullable<Observer<WebXRControllerComponent>>;
+        };
+    } = {};
+
     /**
      * constructs a new anchor system
      * @param _xrSessionManager an instance of WebXRSessionManager
      * @param _options configuration object for this feature
      */
     constructor(private _xrSessionManager: WebXRSessionManager, private _options: IWebXRTeleportationOptions) {
-        // set defaults
-        if (!this._options.rotationAngle) {
-            this._options.rotationAngle = Math.PI / 8;
-        }
-
-        if (!this._options.backwardsTeleportationDistance) {
-            this._options.backwardsTeleportationDistance = 0.5;
-        }
-
         // create default mesh if not provided
         if (!this._options.teleportationTargetMesh) {
             this.createDefaultTargetMesh();
@@ -88,6 +184,18 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         this.setTargetMeshVisibility(false);
     }
 
+    private _selectionFeature: IWebXRFeature;
+
+    /**
+     * This function sets a selection feature that will be disabled when
+     * the forward ray is shown and will be reattached when hidden.
+     * This is used to remove the selection rays when moving.
+     * @param selectionFeature the feature to disable when forward movement is enabled
+     */
+    public setSelectionFeature(selectionFeature: IWebXRFeature) {
+        this._selectionFeature = selectionFeature;
+    }
+
     /**
      * attach this feature
      * Will usually be called by the features manager
@@ -121,19 +229,21 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
                     Quaternion.RotationYawPitchRollToRef(controllerData.teleportationState.currentRotation + controllerData.teleportationState.baseRotation, 0, 0, targetMesh.rotationQuaternion);
                     // set the ray and position
 
+                    let hitPossible = false;
                     // first check if direct ray possible
                     controllerData.xrController.getWorldPointerRayToRef(this._tmpRay);
                     let pick = scene.pickWithRay(this._tmpRay, (o) => {
                         return this._options.floorMeshes.indexOf(o) !== -1;
                     });
                     if (pick && pick.pickedPoint) {
+                        hitPossible = true;
                         this.setTargetMeshPosition(pick.pickedPoint);
                         this.setTargetMeshVisibility(true);
                         this.showParabolicPath(pick);
                     } else {
-                        if (!this._options.disableTeleportationParabolicRay) {
+                        if (this.parabolicRayEnabled) {
                             // check parabolic ray
-                            const radius = this._options.parabolicCheckRadius || 5;
+                            const radius = this.parabolicCheckRadius;
                             this._tmpRay.origin.addToRef(this._tmpRay.direction.scale(radius * 2), this._tmpVector);
                             this._tmpVector.y = this._tmpRay.origin.y;
                             this._tmpRay.origin.addInPlace(this._tmpRay.direction.scale(radius));
@@ -144,6 +254,7 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
                                 return this._options.floorMeshes.indexOf(o) !== -1;
                             });
                             if (pick && pick.pickedPoint) {
+                                hitPossible = true;
                                 this.setTargetMeshPosition(pick.pickedPoint);
                                 this.setTargetMeshVisibility(true);
                                 this.showParabolicPath(pick);
@@ -152,7 +263,7 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
                     }
 
                     // if needed, set visible:
-                    this.setTargetMeshVisibility(true);
+                    this.setTargetMeshVisibility(hitPossible);
                 } else {
                     this.setTargetMeshVisibility(false);
                 }
@@ -165,21 +276,33 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         return true;
     }
 
-    private _controllers: {
-        [controllerUniqueId: string]: {
-            xrController: WebXRController;
-            teleportationComponent?: WebXRControllerComponent;
-            teleportationState: {
-                forward: boolean;
-                backwards: boolean;
-                currentRotation: number;
-                baseRotation: number;
-                rotating: boolean;
-            }
-            onAxisChangedObserver?: Nullable<Observer<IWebXRMotionControllerAxesValue>>;
-            onButtonChangedObserver?: Nullable<Observer<WebXRControllerComponent>>;
-        };
-    } = {};
+    /**
+     * detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    detach(): boolean {
+        this._attached = false;
+
+        if (this._observerTracked) {
+            this._xrSessionManager.onXRFrameObservable.remove(this._observerTracked);
+        }
+
+        Object.keys(this._controllers).forEach((controllerId) => {
+            this._detachController(controllerId);
+        });
+
+        return true;
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    dispose(): void {
+        this.detach();
+        this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.dispose(false, true);
+    }
 
     private _currentTeleportationControllerId: string;
 
@@ -207,11 +330,10 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
             } else {
                 controllerData.onButtonChangedObserver = movementController.onButtonStateChanged.add(() => {
                     if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward && !movementController.touched) {
-                        console.log("in button changed", controllerData.teleportationState, movementController.touched, movementController.changes);
                         controllerData.teleportationState.forward = false;
                         this._currentTeleportationControllerId = "";
                         // do the movement forward here
-                        if (this._options.teleportationTargetMesh) {
+                        if (this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.isVisible) {
                             const height = this._options.xrInput.xrCamera.position.y - this._options.teleportationTargetMesh.position.y;
                             this._options.xrInput.xrCamera.position.copyFrom(this._options.teleportationTargetMesh.position);
                             this._options.xrInput.xrCamera.position.y += height;
@@ -232,7 +354,7 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
                         if (!controllerData.teleportationState.backwards) {
                             controllerData.teleportationState.backwards = true;
                             // teleport backwards ONCE
-                            this._tmpVector.set(0, 0, -this._options.backwardsTeleportationDistance!);
+                            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);
@@ -257,35 +379,23 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
                         if (!controllerData.teleportationState.forward) {
                             if (!controllerData.teleportationState.rotating && Math.abs(axesData.x) > 0.7) {
                                 // rotate in the right direction positive is right
-                                console.log("in rotating", controllerData.teleportationState, movementController.touched, movementController.changes);
                                 controllerData.teleportationState.rotating = true;
-                                const rotation = this._options.rotationAngle! * (axesData.x > 0 ? 1 : -1);
+                                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
-                                setTimeout(() => {
-                                    controllerData.teleportationState.currentRotation = Math.atan2(axesData.x, -axesData.y);
-                                });
+                                if (this.rotationEnabled) {
+                                    setTimeout(() => {
+                                        controllerData.teleportationState.currentRotation = Math.atan2(axesData.x, -axesData.y);
+                                    });
+                                }
                             }
                         }
                     } else {
                         controllerData.teleportationState.rotating = false;
                     }
-
-                    // if (this._currentTeleportationControllerId === controllerData.xrController.uniqueId && controllerData.teleportationState.forward && !movementController.touched) {
-                    //     console.log("in forward axes", controllerData.teleportationState, movementController.touched, movementController.changes);
-                    //     controllerData.teleportationState.forward = false;
-                    //     this._currentTeleportationControllerId = "";
-                    //     // do the movement forward here
-                    //     if (this._options.teleportationTargetMesh) {
-                    //         const height = this._options.xrInput.xrCamera.position.y - this._options.teleportationTargetMesh.position.y;
-                    //         this._options.xrInput.xrCamera.position.copyFrom(this._options.teleportationTargetMesh.position);
-                    //         this._options.xrInput.xrCamera.position.y += height;
-                    //         this._options.xrInput.xrCamera.rotationQuaternion.multiplyInPlace(Quaternion.FromEulerAngles(0, controllerData.teleportationState.currentRotation, 0));
-                    //     }
-                    // }
                 });
             }
         }
@@ -363,6 +473,7 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         }
 
         var cone = CylinderBuilder.CreateCylinder("cone", { diameterTop: 0, tessellation: 4 }, scene);
+        cone.isPickable = false;
         cone.scaling.set(0.5, 0.12, 0.2);
 
         cone.rotate(Axis.X, Math.PI / 2);
@@ -385,8 +496,15 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         this._options.teleportationTargetMesh.getChildren(undefined, false).forEach((m) => { (<any>(m)).isVisible = visible; });
 
         if (!visible) {
-            if (this.quadraticBezierCurve) {
-                this.quadraticBezierCurve.dispose();
+            if (this._quadraticBezierCurve) {
+                this._quadraticBezierCurve.dispose();
+            }
+            if (this._selectionFeature) {
+                this._selectionFeature.attach();
+            }
+        } else {
+            if (this._selectionFeature) {
+                this._selectionFeature.detach();
             }
         }
     }
@@ -397,7 +515,7 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
         this._options.teleportationTargetMesh.position.y += 0.01;
     }
 
-    private quadraticBezierCurve: AbstractMesh;
+    private _quadraticBezierCurve: AbstractMesh;
 
     private showParabolicPath(pickInfo: PickingInfo) {
         if (!pickInfo.pickedPoint) { return; }
@@ -410,40 +528,12 @@ export class WebXRMotionControllerTeleportation implements IWebXRFeature {
             pickInfo.pickedPoint,
             25);
 
-            if (this.quadraticBezierCurve) {
-                this.quadraticBezierCurve.dispose();
-            }
-
-            this.quadraticBezierCurve = LinesBuilder.CreateLines("qbezier", {points: quadraticBezierVectors.getPoints()});
-
-    }
-
-    /**
-     * detach this feature.
-     * Will usually be called by the features manager
-     *
-     * @returns true if successful.
-     */
-    detach(): boolean {
-        this._attached = false;
-
-        if (this._observerTracked) {
-            this._xrSessionManager.onXRFrameObservable.remove(this._observerTracked);
+        if (this._quadraticBezierCurve) {
+            this._quadraticBezierCurve.dispose();
         }
 
-        Object.keys(this._controllers).forEach((controllerId) => {
-            this._detachController(controllerId);
-        });
-
-        return true;
-    }
-
-    /**
-     * Dispose this feature and all of the resources attached
-     */
-    dispose(): void {
-        this.detach();
-        this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.dispose(false, true);
+        this._quadraticBezierCurve = LinesBuilder.CreateLines("path line", { points: quadraticBezierVectors.getPoints() });
+        this._quadraticBezierCurve.isPickable = false;
     }
 }