UNPKG

three-stdlib

Version:

stand-alone library of threejs examples

1 lines 23.7 kB
{"version":3,"file":"MotionControllers.cjs","sources":["../../src/libs/MotionControllers.ts"],"sourcesContent":["/**\n * @webxr-input-profiles/motion-controllers 1.0.0 https://github.com/immersive-web/webxr-input-profiles\n */\n\nimport type { Object3D } from 'three'\n\ninterface GamepadIndices {\n button: number\n xAxis?: number\n yAxis?: number\n}\n\ninterface VisualResponseDescription {\n componentProperty: string\n states: string[]\n valueNodeProperty: string\n valueNodeName: string\n minNodeName?: string\n maxNodeName?: string\n}\n\ntype VisualResponses = Record<string, VisualResponseDescription>\n\ninterface ComponentDescription {\n type: string\n gamepadIndices: GamepadIndices\n rootNodeName: string\n visualResponses: VisualResponses\n touchPointNodeName?: string\n}\n\ninterface Components {\n [componentKey: string]: ComponentDescription\n}\n\ninterface LayoutDescription {\n selectComponentId: string\n components: Components\n gamepadMapping: string\n rootNodeName: string\n assetPath: string\n}\n\ntype Layouts = Partial<Record<XRHandedness, LayoutDescription>>\n\nexport interface Profile {\n profileId: string\n fallbackProfileIds: string[]\n layouts: Layouts\n}\n\ninterface ProfilesList {\n [profileId: string]: { path: string; deprecated?: boolean } | undefined\n}\n\nconst MotionControllerConstants = {\n Handedness: {\n NONE: 'none',\n LEFT: 'left',\n RIGHT: 'right',\n },\n\n ComponentState: {\n DEFAULT: 'default',\n TOUCHED: 'touched',\n PRESSED: 'pressed',\n },\n\n ComponentProperty: {\n BUTTON: 'button',\n X_AXIS: 'xAxis',\n Y_AXIS: 'yAxis',\n STATE: 'state',\n },\n\n ComponentType: {\n TRIGGER: 'trigger',\n SQUEEZE: 'squeeze',\n TOUCHPAD: 'touchpad',\n THUMBSTICK: 'thumbstick',\n BUTTON: 'button',\n },\n\n ButtonTouchThreshold: 0.05,\n\n AxisTouchThreshold: 0.1,\n\n VisualResponseProperty: {\n TRANSFORM: 'transform',\n VISIBILITY: 'visibility',\n },\n}\n\n/**\n * @description Static helper function to fetch a JSON file and turn it into a JS object\n * @param {string} path - Path to JSON file to be fetched\n */\nasync function fetchJsonFile<T>(path: string): Promise<T> {\n const response = await fetch(path)\n if (!response.ok) {\n throw new Error(response.statusText)\n } else {\n return response.json()\n }\n}\n\nasync function fetchProfilesList(basePath: string): Promise<ProfilesList> {\n if (!basePath) {\n throw new Error('No basePath supplied')\n }\n\n const profileListFileName = 'profilesList.json'\n const profilesList = await fetchJsonFile<ProfilesList>(`${basePath}/${profileListFileName}`)\n return profilesList\n}\n\nasync function fetchProfile(\n xrInputSource: XRInputSource,\n basePath: string,\n defaultProfile: string | null = null,\n getAssetPath = true,\n): Promise<{ profile: Profile; assetPath: string | undefined }> {\n if (!xrInputSource) {\n throw new Error('No xrInputSource supplied')\n }\n\n if (!basePath) {\n throw new Error('No basePath supplied')\n }\n\n // Get the list of profiles\n const supportedProfilesList = await fetchProfilesList(basePath)\n\n // Find the relative path to the first requested profile that is recognized\n let match: { profileId: string; profilePath: string; deprecated: boolean } | undefined = undefined\n xrInputSource.profiles.some((profileId: string) => {\n const supportedProfile = supportedProfilesList[profileId]\n if (supportedProfile) {\n match = {\n profileId,\n profilePath: `${basePath}/${supportedProfile.path}`,\n deprecated: !!supportedProfile.deprecated,\n }\n }\n return !!match\n })\n\n if (!match) {\n if (!defaultProfile) {\n throw new Error('No matching profile name found')\n }\n\n const supportedProfile = supportedProfilesList[defaultProfile]\n if (!supportedProfile) {\n throw new Error(`No matching profile name found and default profile \"${defaultProfile}\" missing.`)\n }\n\n match = {\n profileId: defaultProfile,\n profilePath: `${basePath}/${supportedProfile.path}`,\n deprecated: !!supportedProfile.deprecated,\n }\n }\n\n const profile = await fetchJsonFile<Profile>(match.profilePath)\n\n let assetPath: string | undefined = undefined\n if (getAssetPath) {\n let layout\n if ((xrInputSource.handedness as string) === 'any') {\n layout = profile.layouts[Object.keys(profile.layouts)[0] as XRHandedness]\n } else {\n layout = profile.layouts[xrInputSource.handedness]\n }\n if (!layout) {\n throw new Error(`No matching handedness, ${xrInputSource.handedness}, in profile ${match.profileId}`)\n }\n\n if (layout.assetPath) {\n assetPath = match.profilePath.replace('profile.json', layout.assetPath)\n }\n }\n\n return { profile, assetPath }\n}\n\n/** @constant {Object} */\nconst defaultComponentValues = {\n xAxis: 0,\n yAxis: 0,\n button: 0,\n state: MotionControllerConstants.ComponentState.DEFAULT,\n}\n\n/**\n * @description Converts an X, Y coordinate from the range -1 to 1 (as reported by the Gamepad\n * API) to the range 0 to 1 (for interpolation). Also caps the X, Y values to be bounded within\n * a circle. This ensures that thumbsticks are not animated outside the bounds of their physical\n * range of motion and touchpads do not report touch locations off their physical bounds.\n * @param {number | undefined} x The original x coordinate in the range -1 to 1\n * @param {number | undefined} y The original y coordinate in the range -1 to 1\n */\nfunction normalizeAxes(\n x: number | undefined = 0,\n y: number | undefined = 0,\n): { normalizedXAxis: number; normalizedYAxis: number } {\n let xAxis = x\n let yAxis = y\n\n // Determine if the point is outside the bounds of the circle\n // and, if so, place it on the edge of the circle\n const hypotenuse = Math.sqrt(x * x + y * y)\n if (hypotenuse > 1) {\n const theta = Math.atan2(y, x)\n xAxis = Math.cos(theta)\n yAxis = Math.sin(theta)\n }\n\n // Scale and move the circle so values are in the interpolation range. The circle's origin moves\n // from (0, 0) to (0.5, 0.5). The circle's radius scales from 1 to be 0.5.\n const result = {\n normalizedXAxis: xAxis * 0.5 + 0.5,\n normalizedYAxis: yAxis * 0.5 + 0.5,\n }\n return result\n}\n\n/**\n * Contains the description of how the 3D model should visually respond to a specific user input.\n * This is accomplished by initializing the object with the name of a node in the 3D model and\n * property that need to be modified in response to user input, the name of the nodes representing\n * the allowable range of motion, and the name of the input which triggers the change. In response\n * to the named input changing, this object computes the appropriate weighting to use for\n * interpolating between the range of motion nodes.\n */\nclass VisualResponse implements VisualResponseDescription {\n value: number | boolean\n componentProperty: string\n states: string[]\n valueNodeName: string\n valueNodeProperty: string\n minNodeName?: string\n maxNodeName?: string\n valueNode: Object3D | undefined\n minNode: Object3D | undefined\n maxNode: Object3D | undefined\n constructor(visualResponseDescription: VisualResponseDescription) {\n this.componentProperty = visualResponseDescription.componentProperty\n this.states = visualResponseDescription.states\n this.valueNodeName = visualResponseDescription.valueNodeName\n this.valueNodeProperty = visualResponseDescription.valueNodeProperty\n\n if (this.valueNodeProperty === MotionControllerConstants.VisualResponseProperty.TRANSFORM) {\n this.minNodeName = visualResponseDescription.minNodeName\n this.maxNodeName = visualResponseDescription.maxNodeName\n }\n\n // Initializes the response's current value based on default data\n this.value = 0\n this.updateFromComponent(defaultComponentValues)\n }\n\n /**\n * Computes the visual response's interpolation weight based on component state\n * @param {Object} componentValues - The component from which to update\n * @param {number | undefined} xAxis - The reported X axis value of the component\n * @param {number | undefined} yAxis - The reported Y axis value of the component\n * @param {number | undefined} button - The reported value of the component's button\n * @param {string} state - The component's active state\n */\n updateFromComponent({\n xAxis,\n yAxis,\n button,\n state,\n }: {\n xAxis?: number\n yAxis?: number\n button?: number\n state: string\n }): void {\n const { normalizedXAxis, normalizedYAxis } = normalizeAxes(xAxis, yAxis)\n switch (this.componentProperty) {\n case MotionControllerConstants.ComponentProperty.X_AXIS:\n this.value = this.states.includes(state) ? normalizedXAxis : 0.5\n break\n case MotionControllerConstants.ComponentProperty.Y_AXIS:\n this.value = this.states.includes(state) ? normalizedYAxis : 0.5\n break\n case MotionControllerConstants.ComponentProperty.BUTTON:\n this.value = this.states.includes(state) && button ? button : 0\n break\n case MotionControllerConstants.ComponentProperty.STATE:\n if (this.valueNodeProperty === MotionControllerConstants.VisualResponseProperty.VISIBILITY) {\n this.value = this.states.includes(state)\n } else {\n this.value = this.states.includes(state) ? 1.0 : 0.0\n }\n break\n default:\n throw new Error(`Unexpected visualResponse componentProperty ${this.componentProperty}`)\n }\n }\n}\n\nclass Component implements ComponentDescription {\n id: string\n values: {\n state: string\n button: number | undefined\n xAxis: number | undefined\n yAxis: number | undefined\n }\n\n type: string\n gamepadIndices: GamepadIndices\n rootNodeName: string\n visualResponses: Record<string, VisualResponse>\n touchPointNodeName?: string | undefined\n touchPointNode?: Object3D\n\n /**\n * @param {string} componentId - Id of the component\n * @param {InputProfileComponent} componentDescription - Description of the component to be created\n */\n constructor(componentId: string, componentDescription: ComponentDescription) {\n if (\n !componentId ||\n !componentDescription ||\n !componentDescription.visualResponses ||\n !componentDescription.gamepadIndices ||\n Object.keys(componentDescription.gamepadIndices).length === 0\n ) {\n throw new Error('Invalid arguments supplied')\n }\n\n this.id = componentId\n this.type = componentDescription.type\n this.rootNodeName = componentDescription.rootNodeName\n this.touchPointNodeName = componentDescription.touchPointNodeName\n\n // Build all the visual responses for this component\n this.visualResponses = {}\n Object.keys(componentDescription.visualResponses).forEach((responseName) => {\n const visualResponse = new VisualResponse(componentDescription.visualResponses[responseName])\n this.visualResponses[responseName] = visualResponse\n })\n\n // Set default values\n this.gamepadIndices = Object.assign({}, componentDescription.gamepadIndices)\n\n this.values = {\n state: MotionControllerConstants.ComponentState.DEFAULT,\n button: this.gamepadIndices.button !== undefined ? 0 : undefined,\n xAxis: this.gamepadIndices.xAxis !== undefined ? 0 : undefined,\n yAxis: this.gamepadIndices.yAxis !== undefined ? 0 : undefined,\n }\n }\n\n get data(): { id: Component['id'] } & Component['values'] {\n const data = { id: this.id, ...this.values }\n return data\n }\n\n /**\n * @description Poll for updated data based on current gamepad state\n * @param {Object} gamepad - The gamepad object from which the component data should be polled\n */\n updateFromGamepad(gamepad: Gamepad): void {\n // Set the state to default before processing other data sources\n this.values.state = MotionControllerConstants.ComponentState.DEFAULT\n\n // Get and normalize button\n if (this.gamepadIndices.button !== undefined && gamepad.buttons.length > this.gamepadIndices.button) {\n const gamepadButton = gamepad.buttons[this.gamepadIndices.button]\n this.values.button = gamepadButton.value\n this.values.button = this.values.button! < 0 ? 0 : this.values.button\n this.values.button = this.values.button! > 1 ? 1 : this.values.button\n\n // Set the state based on the button\n if (gamepadButton.pressed || this.values.button === 1) {\n this.values.state = MotionControllerConstants.ComponentState.PRESSED\n } else if (gamepadButton.touched || this.values.button! > MotionControllerConstants.ButtonTouchThreshold) {\n this.values.state = MotionControllerConstants.ComponentState.TOUCHED\n }\n }\n\n // Get and normalize x axis value\n if (this.gamepadIndices.xAxis !== undefined && gamepad.axes.length > this.gamepadIndices.xAxis) {\n this.values.xAxis = gamepad.axes[this.gamepadIndices.xAxis]\n this.values.xAxis = this.values.xAxis! < -1 ? -1 : this.values.xAxis\n this.values.xAxis = this.values.xAxis! > 1 ? 1 : this.values.xAxis\n\n // If the state is still default, check if the xAxis makes it touched\n if (\n this.values.state === MotionControllerConstants.ComponentState.DEFAULT &&\n Math.abs(this.values.xAxis!) > MotionControllerConstants.AxisTouchThreshold\n ) {\n this.values.state = MotionControllerConstants.ComponentState.TOUCHED\n }\n }\n\n // Get and normalize Y axis value\n if (this.gamepadIndices.yAxis !== undefined && gamepad.axes.length > this.gamepadIndices.yAxis) {\n this.values.yAxis = gamepad.axes[this.gamepadIndices.yAxis]\n this.values.yAxis = this.values.yAxis! < -1 ? -1 : this.values.yAxis\n this.values.yAxis = this.values.yAxis! > 1 ? 1 : this.values.yAxis\n\n // If the state is still default, check if the yAxis makes it touched\n if (\n this.values.state === MotionControllerConstants.ComponentState.DEFAULT &&\n Math.abs(this.values.yAxis!) > MotionControllerConstants.AxisTouchThreshold\n ) {\n this.values.state = MotionControllerConstants.ComponentState.TOUCHED\n }\n }\n\n // Update the visual response weights based on the current component data\n Object.values(this.visualResponses).forEach((visualResponse) => {\n visualResponse.updateFromComponent(this.values)\n })\n }\n}\n/**\n * @description Builds a motion controller with components and visual responses based on the\n * supplied profile description. Data is polled from the xrInputSource's gamepad.\n * @author Nell Waliczek / https://github.com/NellWaliczek\n */\nclass MotionController {\n xrInputSource: XRInputSource\n assetUrl: string\n layoutDescription: LayoutDescription\n id: string\n components: Record<string, Component>\n /**\n * @param {XRInputSource} xrInputSource - The XRInputSource to build the MotionController around\n * @param {Profile} profile - The best matched profile description for the supplied xrInputSource\n * @param {string} assetUrl\n */\n constructor(xrInputSource: XRInputSource, profile: Profile, assetUrl: string) {\n if (!xrInputSource) {\n throw new Error('No xrInputSource supplied')\n }\n\n if (!profile) {\n throw new Error('No profile supplied')\n }\n\n if (!profile.layouts[xrInputSource.handedness]) {\n throw new Error('No layout for ' + xrInputSource.handedness + ' handedness')\n }\n\n this.xrInputSource = xrInputSource\n this.assetUrl = assetUrl\n this.id = profile.profileId\n\n // Build child components as described in the profile description\n this.layoutDescription = profile.layouts[xrInputSource.handedness]!\n\n this.components = {}\n Object.keys(this.layoutDescription.components).forEach((componentId) => {\n const componentDescription = this.layoutDescription.components[componentId]\n this.components[componentId] = new Component(componentId, componentDescription)\n })\n\n // Initialize components based on current gamepad state\n this.updateFromGamepad()\n }\n\n get gripSpace(): XRInputSource['gripSpace'] {\n return this.xrInputSource.gripSpace\n }\n\n get targetRaySpace(): XRInputSource['targetRaySpace'] {\n return this.xrInputSource.targetRaySpace\n }\n\n /**\n * @description Returns a subset of component data for simplified debugging\n */\n get data(): Array<Component['data']> {\n const data: Array<Component['data']> = []\n Object.values(this.components).forEach((component) => {\n data.push(component.data)\n })\n return data\n }\n\n /**\n * @description Poll for updated data based on current gamepad state\n */\n updateFromGamepad(): void {\n Object.values(this.components).forEach((component) => {\n component.updateFromGamepad(this.xrInputSource.gamepad!)\n })\n }\n}\n\nexport { MotionControllerConstants, MotionController, fetchProfile, fetchProfilesList }\n"],"names":[],"mappings":"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"}