|
@@ -10,7 +10,155 @@ 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
|
|
|
+/**
|
|
|
+ * 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
|
|
|
+ */
|
|
|
+ public static MODEL_BASE_URL: string = 'https://controllers.babylonjs.com/oculus/';
|
|
|
+ /**
|
|
|
+ * The name of the left controller model file
|
|
|
+ */
|
|
|
+ public static MODEL_LEFT_FILENAME: string = 'left.babylon';
|
|
|
+ /**
|
|
|
+ * The name of the right controller model file
|
|
|
+ */
|
|
|
+ public static MODEL_RIGHT_FILENAME: string = 'right.babylon';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Base Url for the Quest controller model.
|
|
|
+ */
|
|
|
+ public static QUEST_MODEL_BASE_URL: string = 'https://controllers.babylonjs.com/oculusQuest/';
|
|
|
+
|
|
|
+ public profileId = "oculus-touch";
|
|
|
+
|
|
|
+ private _modelRootNode: AbstractMesh;
|
|
|
+
|
|
|
+ constructor(scene: Scene,
|
|
|
+ gamepadObject: IMinimalMotionControllerObject,
|
|
|
+ handness: MotionControllerHandness,
|
|
|
+ legacyMapping: boolean = false,
|
|
|
+ private _forceLegacyControllers: boolean = false) {
|
|
|
+ super(scene, legacyMapping ? OculusTouchLegacyLayouts[handness] : OculusTouchLayouts[handness], gamepadObject, handness);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _processLoadedModel(_meshes: AbstractMesh[]): void {
|
|
|
+
|
|
|
+ /*const isQuest = this._isQuest();
|
|
|
+ const triggerDirection = this.handness === 'right' ? -1 : 1;
|
|
|
+
|
|
|
+ this.layout.gamepad!.buttons.forEach((buttonName) => {
|
|
|
+ const comp = buttonName && this.getComponent(buttonName);
|
|
|
+ if (comp) {
|
|
|
+ comp.onButtonStateChanged.add((component) => {
|
|
|
+
|
|
|
+ if (!this.rootMesh) { return; }
|
|
|
+
|
|
|
+ switch (buttonName) {
|
|
|
+ case "xr-standard-trigger": // index trigger
|
|
|
+ if (!isQuest) {
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[3])).rotation.x = -component.value * 0.20;
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[3])).position.y = -component.value * 0.005;
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[3])).position.z = -component.value * 0.005;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ case "xr-standard-squeeze": // secondary trigger
|
|
|
+ if (!isQuest) {
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[4])).position.x = triggerDirection * component.value * 0.0035;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ case "xr-standard-thumbstick": // thumbstick
|
|
|
+ return;
|
|
|
+ case "a-button":
|
|
|
+ case "x-button":
|
|
|
+ if (!isQuest) {
|
|
|
+ if (component.pressed) {
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[1])).position.y = -0.001;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[1])).position.y = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ case "b-button":
|
|
|
+ case "y-button":
|
|
|
+ if (!isQuest) {
|
|
|
+ if (component.pressed) {
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[2])).position.y = -0.001;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ (<AbstractMesh>(this._modelRootNode.getChildren()[2])).position.y = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }, undefined, true);
|
|
|
+ }
|
|
|
+ });*/
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _getFilenameAndPath(): { filename: string; path: string; } {
|
|
|
+ let filename = "";
|
|
|
+ if (this.handness === 'left') {
|
|
|
+ filename = WebXROculusTouchMotionController.MODEL_LEFT_FILENAME;
|
|
|
+ }
|
|
|
+ else { // Right is the default if no hand is specified
|
|
|
+ filename = WebXROculusTouchMotionController.MODEL_RIGHT_FILENAME;
|
|
|
+ }
|
|
|
+
|
|
|
+ let path = this._isQuest() ? WebXROculusTouchMotionController.QUEST_MODEL_BASE_URL : WebXROculusTouchMotionController.MODEL_BASE_URL;
|
|
|
+ return {
|
|
|
+ filename,
|
|
|
+ path
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Is this the new type of oculus touch. At the moment both have the same profile and it is impossible to differentiate
|
|
|
+ * between the touch and touch 2.
|
|
|
+ */
|
|
|
+ private _isQuest() {
|
|
|
+ // this is SADLY the only way to currently check. Until proper profiles will be available.
|
|
|
+ return !!navigator.userAgent.match(/Quest/gi) && !this._forceLegacyControllers;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _updateModel(): void {
|
|
|
+ // no-op. model is updated using observables.
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _getModelLoadingConstraints(): boolean {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _setRootMesh(meshes: AbstractMesh[]): void {
|
|
|
+ this.rootMesh = new Mesh(this.profileId + " " + this.handness, this.scene);
|
|
|
+ this.rootMesh.rotationQuaternion = Quaternion.FromEulerAngles(0, Math.PI, 0);
|
|
|
+
|
|
|
+ meshes.forEach((mesh) => { mesh.isPickable = false; });
|
|
|
+ if (this._isQuest()) {
|
|
|
+ this._modelRootNode = meshes[0];
|
|
|
+ } else {
|
|
|
+ this._modelRootNode = meshes[1];
|
|
|
+ this.rootMesh.position.y = 0.034;
|
|
|
+ this.rootMesh.position.z = 0.052;
|
|
|
+ }
|
|
|
+ this._modelRootNode.parent = this.rootMesh;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// register the profile
|
|
|
+WebXRMotionControllerManager.RegisterController("oculus-touch", (xrInput: XRInputSource, scene: Scene) => {
|
|
|
+ return new WebXROculusTouchMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness);
|
|
|
+});
|
|
|
+
|
|
|
+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",
|
|
@@ -295,153 +443,4 @@ const OculusTouchLegacyLayouts: IMotionControllerLayoutMap = {
|
|
|
"rootNodeName": "oculus-touch-v2-right",
|
|
|
"assetPath": "right.glb"
|
|
|
}
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * 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
|
|
|
- */
|
|
|
- public static MODEL_BASE_URL: string = 'https://controllers.babylonjs.com/oculus/';
|
|
|
- /**
|
|
|
- * The name of the left controller model file
|
|
|
- */
|
|
|
- public static MODEL_LEFT_FILENAME: string = 'left.babylon';
|
|
|
- /**
|
|
|
- * The name of the right controller model file
|
|
|
- */
|
|
|
- public static MODEL_RIGHT_FILENAME: string = 'right.babylon';
|
|
|
-
|
|
|
- /**
|
|
|
- * Base Url for the Quest controller model.
|
|
|
- */
|
|
|
- public static QUEST_MODEL_BASE_URL: string = 'https://controllers.babylonjs.com/oculusQuest/';
|
|
|
-
|
|
|
- public profileId = "oculus-touch";
|
|
|
-
|
|
|
- private _modelRootNode: AbstractMesh;
|
|
|
-
|
|
|
- constructor(scene: Scene,
|
|
|
- gamepadObject: IMinimalMotionControllerObject,
|
|
|
- handness: MotionControllerHandness,
|
|
|
- legacyMapping: boolean = false,
|
|
|
- private _forceLegacyControllers: boolean = false) {
|
|
|
- super(scene, legacyMapping ? OculusTouchLegacyLayouts[handness] : OculusTouchLayouts[handness], gamepadObject, handness);
|
|
|
- }
|
|
|
-
|
|
|
- protected _processLoadedModel(_meshes: AbstractMesh[]): void {
|
|
|
-
|
|
|
- /*const isQuest = this._isQuest();
|
|
|
- const triggerDirection = this.handness === 'right' ? -1 : 1;
|
|
|
-
|
|
|
- this.layout.gamepad!.buttons.forEach((buttonName) => {
|
|
|
- const comp = buttonName && this.getComponent(buttonName);
|
|
|
- if (comp) {
|
|
|
- comp.onButtonStateChanged.add((component) => {
|
|
|
-
|
|
|
- if (!this.rootMesh) { return; }
|
|
|
-
|
|
|
- switch (buttonName) {
|
|
|
- case "xr-standard-trigger": // index trigger
|
|
|
- if (!isQuest) {
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[3])).rotation.x = -component.value * 0.20;
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[3])).position.y = -component.value * 0.005;
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[3])).position.z = -component.value * 0.005;
|
|
|
- }
|
|
|
- return;
|
|
|
- case "xr-standard-squeeze": // secondary trigger
|
|
|
- if (!isQuest) {
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[4])).position.x = triggerDirection * component.value * 0.0035;
|
|
|
- }
|
|
|
- return;
|
|
|
- case "xr-standard-thumbstick": // thumbstick
|
|
|
- return;
|
|
|
- case "a-button":
|
|
|
- case "x-button":
|
|
|
- if (!isQuest) {
|
|
|
- if (component.pressed) {
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[1])).position.y = -0.001;
|
|
|
- }
|
|
|
- else {
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[1])).position.y = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- return;
|
|
|
- case "b-button":
|
|
|
- case "y-button":
|
|
|
- if (!isQuest) {
|
|
|
- if (component.pressed) {
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[2])).position.y = -0.001;
|
|
|
- }
|
|
|
- else {
|
|
|
- (<AbstractMesh>(this._modelRootNode.getChildren()[2])).position.y = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- }, undefined, true);
|
|
|
- }
|
|
|
- });*/
|
|
|
- }
|
|
|
-
|
|
|
- protected _getFilenameAndPath(): { filename: string; path: string; } {
|
|
|
- let filename = "";
|
|
|
- if (this.handness === 'left') {
|
|
|
- filename = WebXROculusTouchMotionController.MODEL_LEFT_FILENAME;
|
|
|
- }
|
|
|
- else { // Right is the default if no hand is specified
|
|
|
- filename = WebXROculusTouchMotionController.MODEL_RIGHT_FILENAME;
|
|
|
- }
|
|
|
-
|
|
|
- let path = this._isQuest() ? WebXROculusTouchMotionController.QUEST_MODEL_BASE_URL : WebXROculusTouchMotionController.MODEL_BASE_URL;
|
|
|
- return {
|
|
|
- filename,
|
|
|
- path
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Is this the new type of oculus touch. At the moment both have the same profile and it is impossible to differentiate
|
|
|
- * between the touch and touch 2.
|
|
|
- */
|
|
|
- private _isQuest() {
|
|
|
- // this is SADLY the only way to currently check. Until proper profiles will be available.
|
|
|
- return !!navigator.userAgent.match(/Quest/gi) && !this._forceLegacyControllers;
|
|
|
- }
|
|
|
-
|
|
|
- protected _updateModel(): void {
|
|
|
- // no-op. model is updated using observables.
|
|
|
- }
|
|
|
-
|
|
|
- protected _getModelLoadingConstraints(): boolean {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- protected _setRootMesh(meshes: AbstractMesh[]): void {
|
|
|
- this.rootMesh = new Mesh(this.profileId + " " + this.handness, this.scene);
|
|
|
- this.rootMesh.rotationQuaternion = Quaternion.FromEulerAngles(0, Math.PI, 0);
|
|
|
-
|
|
|
- meshes.forEach((mesh) => { mesh.isPickable = false; });
|
|
|
- if (this._isQuest()) {
|
|
|
- this._modelRootNode = meshes[0];
|
|
|
- } else {
|
|
|
- this._modelRootNode = meshes[1];
|
|
|
- this.rootMesh.position.y = 0.034;
|
|
|
- this.rootMesh.position.z = 0.052;
|
|
|
- }
|
|
|
- this._modelRootNode.parent = this.rootMesh;
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-// register the profile
|
|
|
-WebXRMotionControllerManager.RegisterController("oculus-touch", (xrInput: XRInputSource, scene: Scene) => {
|
|
|
- return new WebXROculusTouchMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness);
|
|
|
-});
|
|
|
-
|
|
|
-WebXRMotionControllerManager.RegisterController("oculus-touch-legacy", (xrInput: XRInputSource, scene: Scene) => {
|
|
|
- return new WebXROculusTouchMotionController(scene, <any>(xrInput.gamepad), xrInput.handedness, true);
|
|
|
-});
|
|
|
+};
|