|
@@ -30,38 +30,9 @@ import { UtilityLayerRenderer } from '../../Rendering/utilityLayerRenderer';
|
|
*/
|
|
*/
|
|
export interface IWebXRTeleportationOptions {
|
|
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
|
|
|
|
- * If empty, rotation will still work
|
|
|
|
- */
|
|
|
|
- floorMeshes?: AbstractMesh[];
|
|
|
|
- /**
|
|
|
|
- * 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;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * An array of points to which the teleportation will snap to.
|
|
|
|
- * If the teleportation ray is in the proximity of one of those points, it will be corrected to this point.
|
|
|
|
- */
|
|
|
|
- snapPositions?: Vector3[];
|
|
|
|
- /**
|
|
|
|
- * How close should the teleportation ray be in order to snap to position.
|
|
|
|
- * Default to 0.8 units (meters)
|
|
|
|
- */
|
|
|
|
- snapToPositionRadius?: number;
|
|
|
|
- /**
|
|
|
|
- * Should teleportation move only to snap points
|
|
|
|
|
|
+ * if provided, this scene will be used to render meshes.
|
|
*/
|
|
*/
|
|
- snapPointsOnly?: boolean;
|
|
|
|
|
|
+ customUtilityLayerScene?: Scene;
|
|
/**
|
|
/**
|
|
* Values to configure the default target mesh
|
|
* Values to configure the default target mesh
|
|
*/
|
|
*/
|
|
@@ -87,39 +58,88 @@ export interface IWebXRTeleportationOptions {
|
|
*/
|
|
*/
|
|
torusArrowMaterial?: Material;
|
|
torusArrowMaterial?: Material;
|
|
};
|
|
};
|
|
-
|
|
|
|
/**
|
|
/**
|
|
- * Disable using the thumbstick and use the main component (usuallly trigger) on long press.
|
|
|
|
- * This will be automatically true if the controller doesnt have a thumbstick or touchpad.
|
|
|
|
|
|
+ * 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
|
|
|
|
+ * If empty, rotation will still work
|
|
*/
|
|
*/
|
|
- useMainComponentOnly?: boolean;
|
|
|
|
-
|
|
|
|
|
|
+ floorMeshes?: AbstractMesh[];
|
|
|
|
+ /**
|
|
|
|
+ * use this rendering group id for the meshes (optional)
|
|
|
|
+ */
|
|
|
|
+ renderingGroupId?: number;
|
|
|
|
+ /**
|
|
|
|
+ * Should teleportation move only to snap points
|
|
|
|
+ */
|
|
|
|
+ snapPointsOnly?: boolean;
|
|
|
|
+ /**
|
|
|
|
+ * An array of points to which the teleportation will snap to.
|
|
|
|
+ * If the teleportation ray is in the proximity of one of those points, it will be corrected to this point.
|
|
|
|
+ */
|
|
|
|
+ snapPositions?: Vector3[];
|
|
|
|
+ /**
|
|
|
|
+ * How close should the teleportation ray be in order to snap to position.
|
|
|
|
+ * Default to 0.8 units (meters)
|
|
|
|
+ */
|
|
|
|
+ snapToPositionRadius?: 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;
|
|
/**
|
|
/**
|
|
- * If main component is used (no thumbstick), how long should the "long press" take before teleporting
|
|
|
|
|
|
+ * If main component is used (no thumbstick), how long should the "long press" take before teleport
|
|
*/
|
|
*/
|
|
timeToTeleport?: number;
|
|
timeToTeleport?: number;
|
|
/**
|
|
/**
|
|
- * Should meshes created here be added to a utility layer or the main scene
|
|
|
|
|
|
+ * Disable using the thumbstick and use the main component (usually trigger) on long press.
|
|
|
|
+ * This will be automatically true if the controller doesn't have a thumbstick or touchpad.
|
|
*/
|
|
*/
|
|
- useUtilityLayer?: boolean;
|
|
|
|
-
|
|
|
|
|
|
+ useMainComponentOnly?: boolean;
|
|
/**
|
|
/**
|
|
- * if provided, this scene will be used to render meshes.
|
|
|
|
|
|
+ * Should meshes created here be added to a utility layer or the main scene
|
|
*/
|
|
*/
|
|
- customUtilityLayerScene?: Scene;
|
|
|
|
-
|
|
|
|
|
|
+ useUtilityLayer?: boolean;
|
|
/**
|
|
/**
|
|
- * use this rendering group id for the meshes (optional)
|
|
|
|
|
|
+ * Babylon XR Input class for controller
|
|
*/
|
|
*/
|
|
- renderingGroupId?: number;
|
|
|
|
|
|
+ xrInput: WebXRInput;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * This is a teleportation feature to be used with webxr-enabled motion controllers.
|
|
|
|
|
|
+ * 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 around and rotate in the scene using
|
|
* When enabled and attached, the feature will allow a user to move around and rotate in the scene using
|
|
* the input of the attached controllers.
|
|
* the input of the attached controllers.
|
|
*/
|
|
*/
|
|
export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
|
|
+ private _controllers: {
|
|
|
|
+ [controllerUniqueId: string]: {
|
|
|
|
+ xrController: WebXRInputSource;
|
|
|
|
+ teleportationComponent?: WebXRControllerComponent;
|
|
|
|
+ teleportationState: {
|
|
|
|
+ forward: boolean;
|
|
|
|
+ backwards: boolean;
|
|
|
|
+ currentRotation: number;
|
|
|
|
+ baseRotation: number;
|
|
|
|
+ rotating: boolean;
|
|
|
|
+ }
|
|
|
|
+ onAxisChangedObserver?: Nullable<Observer<IWebXRMotionControllerAxesValue>>;
|
|
|
|
+ onButtonChangedObserver?: Nullable<Observer<WebXRControllerComponent>>;
|
|
|
|
+ };
|
|
|
|
+ } = {};
|
|
|
|
+ private _currentTeleportationControllerId: string;
|
|
|
|
+ private _floorMeshes: AbstractMesh[];
|
|
|
|
+ private _quadraticBezierCurve: AbstractMesh;
|
|
|
|
+ private _selectionFeature: IWebXRFeature;
|
|
|
|
+ private _snapToPositions: Vector3[];
|
|
|
|
+ private _snappedToPoint: boolean = false;
|
|
|
|
+ private _teleportationRingMaterial?: StandardMaterial;
|
|
|
|
+ private _tmpRay = new Ray(new Vector3(), new Vector3());
|
|
|
|
+ private _tmpVector = new Vector3();
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* The module's name
|
|
* The module's name
|
|
*/
|
|
*/
|
|
@@ -132,16 +152,13 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
public static readonly Version = 1;
|
|
public static readonly Version = 1;
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Is rotation enabled when moving forward?
|
|
|
|
- * Disabling this feature will prevent the user from deciding the direction when teleporting
|
|
|
|
|
|
+ * Is movement backwards enabled
|
|
*/
|
|
*/
|
|
- public rotationEnabled: boolean = true;
|
|
|
|
|
|
+ public backwardsMovementEnabled = 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
|
|
|
|
|
|
+ * Distance to travel when moving backwards
|
|
*/
|
|
*/
|
|
- public parabolicRayEnabled: boolean = true;
|
|
|
|
|
|
+ public backwardsTeleportationDistance: number = 0.7;
|
|
/**
|
|
/**
|
|
* The distance from the user to the inspection point in the direction of the controller
|
|
* 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
|
|
* A higher number will allow the user to move further
|
|
@@ -149,71 +166,20 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
*/
|
|
*/
|
|
public parabolicCheckRadius: number = 5;
|
|
public parabolicCheckRadius: number = 5;
|
|
/**
|
|
/**
|
|
- * How much rotation should be applied when rotating right and left
|
|
|
|
- */
|
|
|
|
- public rotationAngle: number = Math.PI / 8;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Is movement backwards enabled
|
|
|
|
- */
|
|
|
|
- public backwardsMovementEnabled = true;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Distance to travel when moving backwards
|
|
|
|
- */
|
|
|
|
- public backwardsTeleportationDistance: number = 0.7;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Add a new mesh to the floor meshes array
|
|
|
|
- * @param mesh the mesh to use as floor mesh
|
|
|
|
|
|
+ * 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 addFloorMesh(mesh: AbstractMesh) {
|
|
|
|
- this._floorMeshes.push(mesh);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ public parabolicRayEnabled: boolean = true;
|
|
/**
|
|
/**
|
|
- * Remove a mesh from the floor meshes array
|
|
|
|
- * @param mesh the mesh to remove
|
|
|
|
|
|
+ * How much rotation should be applied when rotating right and left
|
|
*/
|
|
*/
|
|
- public removeFloorMesh(mesh: AbstractMesh) {
|
|
|
|
- const index = this._floorMeshes.indexOf(mesh);
|
|
|
|
- if (index !== -1) {
|
|
|
|
- this._floorMeshes.splice(index, 1);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ public rotationAngle: number = Math.PI / 8;
|
|
/**
|
|
/**
|
|
- * Remove a mesh from the floor meshes array using its name
|
|
|
|
- * @param name the mesh name to remove
|
|
|
|
|
|
+ * Is rotation enabled when moving forward?
|
|
|
|
+ * Disabling this feature will prevent the user from deciding the direction when teleporting
|
|
*/
|
|
*/
|
|
- 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 _floorMeshes: AbstractMesh[];
|
|
|
|
- private _snapToPositions: Vector3[];
|
|
|
|
-
|
|
|
|
- private _controllers: {
|
|
|
|
- [controllerUniqueId: string]: {
|
|
|
|
- xrController: WebXRInputSource;
|
|
|
|
- teleportationComponent?: WebXRControllerComponent;
|
|
|
|
- teleportationState: {
|
|
|
|
- forward: boolean;
|
|
|
|
- backwards: boolean;
|
|
|
|
- currentRotation: number;
|
|
|
|
- baseRotation: number;
|
|
|
|
- rotating: boolean;
|
|
|
|
- }
|
|
|
|
- onAxisChangedObserver?: Nullable<Observer<IWebXRMotionControllerAxesValue>>;
|
|
|
|
- onButtonChangedObserver?: Nullable<Observer<WebXRControllerComponent>>;
|
|
|
|
- };
|
|
|
|
- } = {};
|
|
|
|
|
|
+ public rotationEnabled: boolean = true;
|
|
|
|
|
|
/**
|
|
/**
|
|
* constructs a new anchor system
|
|
* constructs a new anchor system
|
|
@@ -233,10 +199,6 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
this._setTargetMeshVisibility(false);
|
|
this._setTargetMeshVisibility(false);
|
|
}
|
|
}
|
|
|
|
|
|
- private _selectionFeature: IWebXRFeature;
|
|
|
|
- private _snappedToPoint: boolean = false;
|
|
|
|
- private _teleportationRingMaterial?: StandardMaterial;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Get the snapPointsOnly flag
|
|
* Get the snapPointsOnly flag
|
|
*/
|
|
*/
|
|
@@ -253,6 +215,14 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
+ * Add a new mesh to the floor meshes array
|
|
|
|
+ * @param mesh the mesh to use as floor mesh
|
|
|
|
+ */
|
|
|
|
+ public addFloorMesh(mesh: AbstractMesh) {
|
|
|
|
+ this._floorMeshes.push(mesh);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
* Add a new snap-to point to fix teleportation to this position
|
|
* Add a new snap-to point to fix teleportation to this position
|
|
* @param newSnapPoint The new Snap-To point
|
|
* @param newSnapPoint The new Snap-To point
|
|
*/
|
|
*/
|
|
@@ -260,6 +230,62 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
this._snapToPositions.push(newSnapPoint);
|
|
this._snapToPositions.push(newSnapPoint);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public attach(): boolean {
|
|
|
|
+ if (!super.attach()) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._options.xrInput.controllers.forEach(this._attachController);
|
|
|
|
+ this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable, this._attachController);
|
|
|
|
+ this._addNewAttachObserver(this._options.xrInput.onControllerRemovedObservable, (controller) => {
|
|
|
|
+ // REMOVE the controller
|
|
|
|
+ this._detachController(controller.uniqueId);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public detach(): boolean {
|
|
|
|
+ if (!super.detach()) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Object.keys(this._controllers).forEach((controllerId) => {
|
|
|
|
+ this._detachController(controllerId);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ this._setTargetMeshVisibility(false);
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public dispose(): void {
|
|
|
|
+ super.dispose();
|
|
|
|
+ this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.dispose(false, true);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Remove a mesh from the floor meshes array
|
|
|
|
+ * @param mesh the mesh to remove
|
|
|
|
+ */
|
|
|
|
+ public removeFloorMesh(mesh: AbstractMesh) {
|
|
|
|
+ const index = this._floorMeshes.indexOf(mesh);
|
|
|
|
+ if (index !== -1) {
|
|
|
|
+ this._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);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* This function will iterate through the array, searching for this point or equal to it. It will then remove it from the snap-to array
|
|
* This function will iterate through the array, searching for this point or equal to it. It will then remove it from the snap-to array
|
|
* @param snapPointToRemove the point (or a clone of it) to be removed from the array
|
|
* @param snapPointToRemove the point (or a clone of it) to be removed from the array
|
|
@@ -285,6 +311,7 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
}
|
|
}
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* This function sets a selection feature that will be disabled when
|
|
* This function sets a selection feature that will be disabled when
|
|
* the forward ray is shown and will be reattached when hidden.
|
|
* the forward ray is shown and will be reattached when hidden.
|
|
@@ -295,40 +322,6 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
this._selectionFeature = selectionFeature;
|
|
this._selectionFeature = selectionFeature;
|
|
}
|
|
}
|
|
|
|
|
|
- public attach(): boolean {
|
|
|
|
- if (!super.attach()) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this._options.xrInput.controllers.forEach(this._attachController);
|
|
|
|
- this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable, this._attachController);
|
|
|
|
- this._addNewAttachObserver(this._options.xrInput.onControllerRemovedObservable, (controller) => {
|
|
|
|
- // REMOVE the controller
|
|
|
|
- this._detachController(controller.uniqueId);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public detach(): boolean {
|
|
|
|
- if (!super.detach()) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Object.keys(this._controllers).forEach((controllerId) => {
|
|
|
|
- this._detachController(controllerId);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- this._setTargetMeshVisibility(false);
|
|
|
|
-
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public dispose(): void {
|
|
|
|
- super.dispose();
|
|
|
|
- this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.dispose(false, true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
protected _onXRFrame(_xrFrame: XRFrame) {
|
|
protected _onXRFrame(_xrFrame: XRFrame) {
|
|
const frame = this._xrSessionManager.currentFrame;
|
|
const frame = this._xrSessionManager.currentFrame;
|
|
const scene = this._xrSessionManager.scene;
|
|
const scene = this._xrSessionManager.scene;
|
|
@@ -396,8 +389,6 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private _currentTeleportationControllerId: string;
|
|
|
|
-
|
|
|
|
private _attachController = (xrController: WebXRInputSource) => {
|
|
private _attachController = (xrController: WebXRInputSource) => {
|
|
if (this._controllers[xrController.uniqueId]) {
|
|
if (this._controllers[xrController.uniqueId]) {
|
|
// already attached
|
|
// already attached
|
|
@@ -490,7 +481,6 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
// Teleport the users feet to where they targeted
|
|
// Teleport the users feet to where they targeted
|
|
this._options.xrInput.xrCamera.position.addInPlace(pick.pickedPoint);
|
|
this._options.xrInput.xrCamera.position.addInPlace(pick.pickedPoint);
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (axesData.y < -0.7 && !this._currentTeleportationControllerId && !controllerData.teleportationState.rotating) {
|
|
if (axesData.y < -0.7 && !this._currentTeleportationControllerId && !controllerData.teleportationState.rotating) {
|
|
@@ -527,37 +517,6 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
- private _teleportForward(controllerId: string) {
|
|
|
|
- const controllerData = this._controllers[controllerId];
|
|
|
|
- controllerData.teleportationState.forward = false;
|
|
|
|
- this._currentTeleportationControllerId = "";
|
|
|
|
- if (this.snapPointsOnly && !this._snappedToPoint) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- // do the movement forward here
|
|
|
|
- if (this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.isVisible) {
|
|
|
|
- const height = this._options.xrInput.xrCamera.realWorldHeight;
|
|
|
|
- this._options.xrInput.xrCamera.position.copyFrom(this._options.teleportationTargetMesh.position);
|
|
|
|
- this._options.xrInput.xrCamera.position.y += height;
|
|
|
|
- this._options.xrInput.xrCamera.rotationQuaternion.multiplyInPlace(Quaternion.FromEulerAngles(0, controllerData.teleportationState.currentRotation, 0));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private _detachController(xrControllerUniqueId: string) {
|
|
|
|
- const controllerData = this._controllers[xrControllerUniqueId];
|
|
|
|
- if (!controllerData) { return; }
|
|
|
|
- if (controllerData.teleportationComponent) {
|
|
|
|
- if (controllerData.onAxisChangedObserver) {
|
|
|
|
- controllerData.teleportationComponent.onAxisValueChangedObservable.remove(controllerData.onAxisChangedObserver);
|
|
|
|
- }
|
|
|
|
- if (controllerData.onButtonChangedObserver) {
|
|
|
|
- controllerData.teleportationComponent.onButtonStateChangedObservable.remove(controllerData.onButtonChangedObserver);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- // remove from the map
|
|
|
|
- delete this._controllers[xrControllerUniqueId];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
private _createDefaultTargetMesh() {
|
|
private _createDefaultTargetMesh() {
|
|
// set defaults
|
|
// set defaults
|
|
this._options.defaultTargetMeshOptions = this._options.defaultTargetMeshOptions || {};
|
|
this._options.defaultTargetMeshOptions = this._options.defaultTargetMeshOptions || {};
|
|
@@ -649,6 +608,50 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
this._options.teleportationTargetMesh = teleportationTarget;
|
|
this._options.teleportationTargetMesh = teleportationTarget;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private _detachController(xrControllerUniqueId: string) {
|
|
|
|
+ const controllerData = this._controllers[xrControllerUniqueId];
|
|
|
|
+ if (!controllerData) { return; }
|
|
|
|
+ if (controllerData.teleportationComponent) {
|
|
|
|
+ if (controllerData.onAxisChangedObserver) {
|
|
|
|
+ controllerData.teleportationComponent.onAxisValueChangedObservable.remove(controllerData.onAxisChangedObserver);
|
|
|
|
+ }
|
|
|
|
+ if (controllerData.onButtonChangedObserver) {
|
|
|
|
+ controllerData.teleportationComponent.onButtonStateChangedObservable.remove(controllerData.onButtonChangedObserver);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // remove from the map
|
|
|
|
+ delete this._controllers[xrControllerUniqueId];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private _findClosestSnapPointWithRadius(realPosition: Vector3, radius: number = this._options.snapToPositionRadius || 0.8) {
|
|
|
|
+ let closestPoint: Nullable<Vector3> = null;
|
|
|
|
+ let closestDistance = Number.MAX_VALUE;
|
|
|
|
+ if (this._snapToPositions.length) {
|
|
|
|
+ const radiusSquared = radius * radius;
|
|
|
|
+ this._snapToPositions.forEach((position) => {
|
|
|
|
+ const dist = Vector3.DistanceSquared(position, realPosition);
|
|
|
|
+ if (dist <= radiusSquared && dist < closestDistance) {
|
|
|
|
+ closestDistance = dist;
|
|
|
|
+ closestPoint = position;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ return closestPoint;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private _setTargetMeshPosition(newPosition: Vector3) {
|
|
|
|
+ if (!this._options.teleportationTargetMesh) { return; }
|
|
|
|
+ const snapPosition = this._findClosestSnapPointWithRadius(newPosition);
|
|
|
|
+ this._snappedToPoint = !!snapPosition;
|
|
|
|
+ if (this.snapPointsOnly && !this._snappedToPoint && this._teleportationRingMaterial) {
|
|
|
|
+ this._teleportationRingMaterial.diffuseColor.set(1.0, 0.3, 0.3);
|
|
|
|
+ } else if (this.snapPointsOnly && this._snappedToPoint && this._teleportationRingMaterial) {
|
|
|
|
+ this._teleportationRingMaterial.diffuseColor.set(0.3, 0.3, 1.0);
|
|
|
|
+ }
|
|
|
|
+ this._options.teleportationTargetMesh.position.copyFrom(snapPosition || newPosition);
|
|
|
|
+ this._options.teleportationTargetMesh.position.y += 0.01;
|
|
|
|
+ }
|
|
|
|
+
|
|
private _setTargetMeshVisibility(visible: boolean) {
|
|
private _setTargetMeshVisibility(visible: boolean) {
|
|
if (!this._options.teleportationTargetMesh) { return; }
|
|
if (!this._options.teleportationTargetMesh) { return; }
|
|
if (this._options.teleportationTargetMesh.isVisible === visible) { return; }
|
|
if (this._options.teleportationTargetMesh.isVisible === visible) { return; }
|
|
@@ -669,21 +672,6 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private _setTargetMeshPosition(newPosition: Vector3) {
|
|
|
|
- if (!this._options.teleportationTargetMesh) { return; }
|
|
|
|
- const snapPosition = this._findClosestSnapPointWithRadius(newPosition);
|
|
|
|
- this._snappedToPoint = !!snapPosition;
|
|
|
|
- if (this.snapPointsOnly && !this._snappedToPoint && this._teleportationRingMaterial) {
|
|
|
|
- this._teleportationRingMaterial.diffuseColor.set(1.0, 0.3, 0.3);
|
|
|
|
- } else if (this.snapPointsOnly && this._snappedToPoint && this._teleportationRingMaterial) {
|
|
|
|
- this._teleportationRingMaterial.diffuseColor.set(0.3, 0.3, 1.0);
|
|
|
|
- }
|
|
|
|
- this._options.teleportationTargetMesh.position.copyFrom(snapPosition || newPosition);
|
|
|
|
- this._options.teleportationTargetMesh.position.y += 0.01;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private _quadraticBezierCurve: AbstractMesh;
|
|
|
|
-
|
|
|
|
private _showParabolicPath(pickInfo: PickingInfo) {
|
|
private _showParabolicPath(pickInfo: PickingInfo) {
|
|
if (!pickInfo.pickedPoint) { return; }
|
|
if (!pickInfo.pickedPoint) { return; }
|
|
|
|
|
|
@@ -703,20 +691,20 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature {
|
|
this._quadraticBezierCurve.isPickable = false;
|
|
this._quadraticBezierCurve.isPickable = false;
|
|
}
|
|
}
|
|
|
|
|
|
- private _findClosestSnapPointWithRadius(realPosition: Vector3, radius: number = this._options.snapToPositionRadius || 0.8) {
|
|
|
|
- let closestPoint: Nullable<Vector3> = null;
|
|
|
|
- let closestDistance = Number.MAX_VALUE;
|
|
|
|
- if (this._snapToPositions.length) {
|
|
|
|
- const radiusSquared = radius * radius;
|
|
|
|
- this._snapToPositions.forEach((position) => {
|
|
|
|
- const dist = Vector3.DistanceSquared(position, realPosition);
|
|
|
|
- if (dist <= radiusSquared && dist < closestDistance) {
|
|
|
|
- closestDistance = dist;
|
|
|
|
- closestPoint = position;
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ private _teleportForward(controllerId: string) {
|
|
|
|
+ const controllerData = this._controllers[controllerId];
|
|
|
|
+ controllerData.teleportationState.forward = false;
|
|
|
|
+ this._currentTeleportationControllerId = "";
|
|
|
|
+ if (this.snapPointsOnly && !this._snappedToPoint) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // do the movement forward here
|
|
|
|
+ if (this._options.teleportationTargetMesh && this._options.teleportationTargetMesh.isVisible) {
|
|
|
|
+ const height = this._options.xrInput.xrCamera.realWorldHeight;
|
|
|
|
+ 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));
|
|
}
|
|
}
|
|
- return closestPoint;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|