diff --git a/src/libs/MotionControllers.ts b/src/libs/MotionControllers.ts index 315b8ca3..fa945714 100644 --- a/src/libs/MotionControllers.ts +++ b/src/libs/MotionControllers.ts @@ -10,10 +10,18 @@ interface GamepadIndices { yAxis?: number } +type ComponentState = 'default' | 'touched' | 'pressed' + +type ComponentProperty = 'button' | 'xAxis' | 'yAxis' | 'state' + +type ValueNodeProperty = 'transform' | 'visibility' + +type ComponentType = 'trigger' | 'squeeze' | 'touchpad' | 'thumbstick' | 'button' + interface VisualResponseDescription { - componentProperty: string - states: string[] - valueNodeProperty: string + componentProperty: ComponentProperty + states: ComponentState[] + valueNodeProperty: ValueNodeProperty valueNodeName: string minNodeName?: string maxNodeName?: string @@ -22,7 +30,7 @@ interface VisualResponseDescription { type VisualResponses = Record interface ComponentDescription { - type: string + type: ComponentType gamepadIndices: GamepadIndices rootNodeName: string visualResponses: VisualResponses @@ -58,20 +66,20 @@ const MotionControllerConstants = { NONE: 'none', LEFT: 'left', RIGHT: 'right', - }), + } as const), ComponentState: Object.freeze({ DEFAULT: 'default', TOUCHED: 'touched', PRESSED: 'pressed', - }), + } as const), ComponentProperty: Object.freeze({ BUTTON: 'button', X_AXIS: 'xAxis', Y_AXIS: 'yAxis', STATE: 'state', - }), + } as const), ComponentType: Object.freeze({ TRIGGER: 'trigger', @@ -79,7 +87,7 @@ const MotionControllerConstants = { TOUCHPAD: 'touchpad', THUMBSTICK: 'thumbstick', BUTTON: 'button', - }), + } as const), ButtonTouchThreshold: 0.05, @@ -88,7 +96,7 @@ const MotionControllerConstants = { VisualResponseProperty: Object.freeze({ TRANSFORM: 'transform', VISIBILITY: 'visibility', - }), + } as const), } /** @@ -184,8 +192,15 @@ async function fetchProfile( return { profile, assetPath } } +interface ComponentValues { + button: number | undefined + state: ComponentState + xAxis: number | undefined + yAxis: number | undefined +} + /** @constant {Object} */ -const defaultComponentValues = { +const defaultComponentValues: ComponentValues = { xAxis: 0, yAxis: 0, button: 0, @@ -235,10 +250,10 @@ function normalizeAxes( */ class VisualResponse implements VisualResponseDescription { value: number | boolean - componentProperty: string - states: string[] + componentProperty: ComponentProperty + states: ComponentState[] valueNodeName: string - valueNodeProperty: string + valueNodeProperty: ValueNodeProperty minNodeName?: string maxNodeName?: string valueNode: Object3D | undefined @@ -268,17 +283,7 @@ class VisualResponse implements VisualResponseDescription { * @param {number | undefined} button - The reported value of the component's button * @param {string} state - The component's active state */ - updateFromComponent({ - xAxis, - yAxis, - button, - state, - }: { - xAxis?: number - yAxis?: number - button?: number - state: string - }): void { + updateFromComponent({ xAxis, yAxis, button, state }: ComponentValues): void { const { normalizedXAxis, normalizedYAxis } = normalizeAxes(xAxis, yAxis) switch (this.componentProperty) { case MotionControllerConstants.ComponentProperty.X_AXIS: @@ -305,14 +310,9 @@ class VisualResponse implements VisualResponseDescription { class Component implements ComponentDescription { id: string - values: { - state: string - button: number | undefined - xAxis: number | undefined - yAxis: number | undefined - } + values: ComponentValues - type: string + type: ComponentType gamepadIndices: GamepadIndices rootNodeName: string visualResponses: Record diff --git a/src/webxr/OculusHandModel.ts b/src/webxr/OculusHandModel.ts index 5b84deab..affd35a2 100644 --- a/src/webxr/OculusHandModel.ts +++ b/src/webxr/OculusHandModel.ts @@ -29,6 +29,7 @@ class OculusHandModel extends Object3D { this.xrInputSource = null controller.addEventListener('connected', (event) => { + console.log('OculusHandModel connected') const xrInputSource = event.data if (xrInputSource.hand && !this.motionController) { @@ -45,6 +46,7 @@ class OculusHandModel extends Object3D { }) controller.addEventListener('disconnected', () => { + console.log('OculusHandModel disconnected') this.dispose() }) } diff --git a/src/webxr/XRControllerModelFactory.ts b/src/webxr/XRControllerModelFactory.ts index 29f7b224..77576dce 100644 --- a/src/webxr/XRControllerModelFactory.ts +++ b/src/webxr/XRControllerModelFactory.ts @@ -154,9 +154,9 @@ class XRControllerModelFactory { gltfLoader: GLTFLoader path: string private _assetCache: Record - constructor(gltfLoader: GLTFLoader = null) { + constructor(gltfLoader: GLTFLoader = null, path = DEFAULT_PROFILES_PATH) { this.gltfLoader = gltfLoader - this.path = DEFAULT_PROFILES_PATH + this.path = path this._assetCache = {} // If a GLTFLoader wasn't supplied to the constructor create a new one. @@ -169,6 +169,17 @@ class XRControllerModelFactory { const controllerModel = new XRControllerModel() let scene: Object3D | null = null + const onDisconnected = (): void => { + controllerModel.motionController = null + controller.dispatchEvent({ + type: 'motionControllerDestroyed', + }) + if (scene) { + controllerModel.remove(scene) + } + scene = null + } + const onConnected = (event: any): void => { const xrInputSource = event.data @@ -179,8 +190,11 @@ class XRControllerModelFactory { if (!assetPath) { throw new Error('no asset path') } - controllerModel.motionController = new MotionController(xrInputSource, profile, assetPath) + controller.dispatchEvent({ + type: 'motionControllerCreated', + data: controllerModel.motionController, + }) const assetUrl = controllerModel.motionController.assetUrl @@ -189,6 +203,10 @@ class XRControllerModelFactory { scene = cachedAsset.scene.clone() addAssetSceneToControllerModel(controllerModel, scene) + controller.dispatchEvent({ + type: 'motionControllerLinkedToScene', + data: controllerModel.motionController, + }) } else { if (!this.gltfLoader) { throw new Error('GLTFLoader not set.') @@ -208,6 +226,10 @@ class XRControllerModelFactory { scene = asset.scene.clone() addAssetSceneToControllerModel(controllerModel, scene) + controller.dispatchEvent({ + type: 'motionControllerLinkedToScene', + data: controllerModel.motionController, + }) }, null, () => { @@ -222,17 +244,6 @@ class XRControllerModelFactory { } controller.addEventListener('connected', onConnected) - - const onDisconnected = (): void => { - controller.removeEventListener('connected', onConnected) - controller.removeEventListener('disconnected', onDisconnected) - controllerModel.motionController = null - if (scene) { - controllerModel.remove(scene) - } - scene = null - } - controller.addEventListener('disconnected', onDisconnected) return controllerModel